好了,看了标题我知道你有疑问,这里我得承认算并半个标题党吧。
事情是这样的:
这里有段程序,你跑一下,结果可能跟你想的不一样
1 public static void main(String[] args) {
2 String str = "䕫";
3 System.out.println(str.length());
4 }
5
6```java
7
8你可能认为字符串长度应该是1吧,为什么会是2呢?这里其实就是所谓的『坑』,说到这个坑,话就有些长了,我们先看一些关于字符的概念。
9
10以下的基础知识我相信大多数开发的同学都知道,如果你明白直接跳过就好。
11
12* * *
13
14Unicode 字符集的出现就是为了统一编码。所谓字符集就是一个由众多不同的字符组成的集合。
15
16Unicode 字符集对每一个字符都分配了一个唯一的 代码点(code point) 用来标识字符本身。
17
18所谓代码点就是一个添加了 U+ 前缀的十六进制整数,如字母 A 的代码点就是 U+0041。
19
20有了Unicode 字符集后,我们要考虑的就是以什么样的方式对这些字符进行传输和存储,这就是 Unicode 编码的实现方式,我们称为 Unicode 转换格式(Unicode Transformation Format,简称 UTF)。我们熟悉的 UTF-8、 UTF-16 等就是不同的 Unicode编码实现方式。
21
22**码点如何转换成UTF的几种形式呢?**
23
24****
25
26**如上图所示**
27
28- **UTF-32采用的定长四字节则是32位**
29
30- **UTF-8是变长的编码方案,可以有1,2,3,4四种字节组合**
31
32- **UTF-16是一种变长的2或4字节编码模式**
33
34在 Unicode 字符集诞生之初,采用 UCS-2(2-byte Universal Character Set) 这种定长的编码方式对 Unicode 字符集进行编码,这种方式采用 16 bit 的长度来进行字符编码,所以最多可以对 2^16 = 65536 个字符进行编码(编码范围从 U+0000 ~ U+FFFF)。在当时的情况下,设计者们用了不到一半的数量就对所有字符进行了编码,并且认为剩余的空间足够用于未来新增字符的编码。
35
36不幸的是,随着中文、日文、韩文等表意文字不断的加入,Unicode 字符集中的字符数量很快超过了 16 位所能编码的最大字符数量,于是设计者们对 Unicode 字符集进行了新的设计。
37
38新的设计将字符集中的所有字符分为 17 个 代码平面(code plane)。其中 U+0000 ~ U+FFFF 这个代码点范围被划定为 基本多语言平面(Basic MultilingualPlane,简记为 BMP,如下图第一个花花绿绿的那个),其余的字符分别划入 16 个 辅助平面(Supplementary Plane),代码点范围为 U+10000 ~ U+10FFFF,这些处于辅助平面的字符我们称作 **增补字符**(supplementary characters)。
39
40
41
42在 Unicode 字符集中的字符被重新划分到不同平面后,需要注意:
43
44BMP 范围内的字符和 UCS-2 下的字符编码基本保持一致,但是 BMP 中的 U+D800 ~ U+DFFF 部分被留空,不分配给任何字符,作用是用于给辅助平面内的字符进行编码。
45
46不是每个平面内的每个位置都被分配给了指定的字符,原因是:
47
48特殊用途,如 BMP 中的 U+D800 ~ U+DFFF 部分;
49
50- 作为保留空间
51
52- 没有足够的字符
53
54* * *
55
56**回答程序输出长度为2而不是1的问题**
57
58我们使用的字符其实不是普通字符,而是增补字符,我们知道 Java 中 char 的长度永远是 16 位,如果我们在字符串中使用了增补字符,那就意味着需要 2 个 char 类型的长度才能存储,对于 String 底层存储字符的数组 value 来说,就需要 2 个数组元素的位置。我们再看一下String 类length方法的源码:
59
60```cs
61/**
62 * Returns the length of this string.
63 * The length is equal to the number of <a href="Character.html#unicode">Unicode
64 * code units</a> in the string.
65 *
66 * @return the length of the sequence of characters represented by this
67 * object.
68 */
69public int length() {
70return value.length;
71 }
一切就明白了。java 的 String 内部用的 UTF-16 编码,String.length() 直接返回 code unit 的个数,也就是 Java 的 2 字节 char 的个数。
当然这里不是说绝对不要用char,只是坑多(上面只是其中一个,JDK9还有别的
),建议少用而已。
