结论:
java
中的char类型表示的是UTF-16
编码中的一个码元,所以一个char类型只能表示第0级别(BMP
)中的符号,其他级别中的符号需要用两个char
变量才能表示。char用U+xxxx
的方式赋值时,规定xxxx的范围是[0000->FFFF]
,所以补充平面中的符号是不能直接通过U形式的赋值到一个char
变量直接得到的(因为需要两个变量)。java
中的char
及它的包装类、String
、StringBuffer
类使用UTF-16
编码,在字节码中使用改进的utf-8
编码,而在字节码加载到内存中执行的时候,又把缩略码转化为UTF-16
的表示形式。字节码改变形式可能是因为出于对节省空间缩小文件体积的考量。- 关于
java
中String
内部存储方式发生变化的说明:- 在
jdk9
之后,String
的内部存储由char[]
改为了byte[]
和一个标志位,这个改变有利于节省空间。
- 在

一、Unicode
Unicode
是为了统一不同字符间的交互与编码问题而制定的一种规范,旨在对世界上所有可数字化的书写系统中的所有文字进行文本编码。
在Unicode
规范中,每一个可书写字符都有一个唯一码点(code point
),也就是说每一个字符都由一个码点唯一确定。
Unicode
可使用多种编码方式进行处理和存储二进制数据,Unicode
标准中主要 定义了三种编码方式:UTF-8
、UTF-16
与UTF-32
,其中UTF-8
的使用最为广泛。
二、UTF-8编码规则
练习打字的时候有一个常识,即汉字的大部分文本都是由常见的前500字组成的,如果要练打字速度的话,只需要把常见的前500个字练好就可以了。为了保险起见,可以练习常见的前1500个字。也就是"二八定律"。
同样,在编码中,数值较低的码点代表的符号出现的频率通常较高,因此低码点的数字需尽量的使用较少的字节进行编码。ASCII
码的码点范围是从0-127共128个数,在UTF-8
编码中,ASCII
码的编码需与其自身相同。
UTF-8
可以使用一个字节、二个字节、三个字节与四个字节进行编码。这四种编码方式的表示范围与编码方式如下所示。

从这个表中可以看出:
- 使用一个字节编码时,最高位取0,用其余七个数来表示具体的值,正好是128个,与
ASCII
码范围完全一致。 - 首字节的前几位已固定,如果1的个数是
n
,则使用n
个字节来编码。非首字节以10开始。
具体的编码规则为:

以'中'的编码为例:

通过java程序可以得出'中'的码点为0x4E2D
,UTF-8
编码为:U+E4B8AD
。 与按照编码规则手动计算的结果一致:

java中对于char类型和String类型使用的是utf-16编码,在字节码文件中常量池中使用的是utf-8的缩略编码,3个字节的缩略编码与正常编码是一样的,所以'中'字在字节码文件中一定也是正常utf-8的三字节表示方式;python中使用的是utf-8编码;C语言通过设置编码格式也可以使得char类型使用utf-8编码。
java中的验证

字节码文件中可以看到第一行为sipush 20013
,即将20013入栈。下一个执行的指令为istore_1
,即从操作数栈中弹出栈顶的int
(此处就是20013),然后将弹出的数存储到局部变量表索引为1的位置。
查看局部变量表可知,索引为0的位置存储的是main方法的形参args,1位置存储的是局部变量c,即将20013赋值给c变量。此处的20013转换为16进制就是
0x4e2d
.
三、UTF-16编码规则
Unicode中的Plane(平面)
在Unicode
标准中,平面是由65536 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 2 16 ) (2^{16}) </math>(216)个连续码点组成的组。将所有码点划分为17个平面,对于16进制的00至10。其中第0个平面称为BMP
(Basic Multilingual Plane
),包含了最常用的字符。其余的16个平面称为补充平面。这17个平面中的最后一个码点为:U+10FFFF
.
UTF-16
编码的长度也是可变的,可以使用一个或者两个16-bit
的代码单元(也就是2字节或者4字节)。
在这个概念下,第0个级别的范围为U+0000->U+0FFFF
,将这个级别分为三个范围:
U+0000->U+D7FF
U+D800->U+DFFF
U+E000->U+FFFF
编码规则为:
-
在
BMP
中,用两个字节表示 -
在补充平面中,用四个字节表示
- 补充平面的表示范围为:
U+10000->U+10FFFF
,这个范围内的码点编码时需要先减去U+10000,减完之后,范围就会变为[U+0000->U+FFFFF]
,也就是2^{20}
个数,需要用20位来表示。 - 把这20位拆成高10位和低10位,高10位需要加上0xD800。这16位的表示范围就会变为:
[U+D800->0xDBFF]
对应于0级别的第二个范围的前半部分。
- 低10位加上0xDC00,表数范围就会变为:
[U+DC00->DFFFF]
,对应于0级别的第二个范围的后半部分。与高10位组合出来的范围恰好完整构成0级别的整个第2段范围。
- 补充平面的表示范围为:
java中的验证

