char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).
首先,让我们先看个例子:
输出结果:
我们设置的字符串都是两个unicode字符,输出结果:
- 普通的中文字:字符串的长度是2,每个中文字按UTF-8编码是三个字节,字符数组的长度看起来也没问题
- emojis字符:我们设置了两个emojis字符,男女头像。结果字符串的长度是4, UTF-8编码8个字节,字符数组的长度是4
- 生僻的中文字:我们设置了两个中文字,其中一个是生僻的中文字。结果字符串的长度是3, UTF-8编码7个字节,字符数组的长度是3
这还得从Java的历史说起。
直到Unicode 3.0, Java用两个字节来表示unicode字符还没有问题,因为Unicode 3.0最多49,259个字符, 两个字节可以表示65,535个字符,还足够容的下所有的uicode3.0字符。
在Unicode中,为每一个字符对应一个编码点(一个整数),用 U+紧跟着十六进制数表示。所有字符按照使用上的频繁度划分为 17 个平面(编号为 0-16),即基本的多语言平面和增补平面。基本的多语言平面(英文为 Basic Multilingual Plane,简称 BMP)又称平面 0,收集了使用最广泛的字符。
Java的字符在内部以UTF-16编码方式来表示,String.length返回的是Code Unit的长度,而不再是Unicode中字符的长度。对于传统的BMP平面的代码点,String.length和我们传统理解的字符的数量是一致的,对于扩展的字符,String.length可能是我们理解的字符长度的两倍。
其实是不会的, 幸运的是, 在BMP平面中, U+D800到U+DFFF之间的码位是永久保留不映射到Unicode字符,UTF-16就利用保留下来的0xD800-0xDFFF区块的码位来对辅助平面的字符的码位进行编码。
可以看到前导代理和后尾代理的范围都落在了BMP平面中不用来映射的码位,所以不会产生冲突,而且前导代理和后尾代理也没有重合。这样我们得到两个字节的,就可以直接判断它是否是BMP平面的字符,还是扩展字符中的前导代理还是后尾代码。
我们在进行字符串截取的时候,比如String.substring有可能会踩到一些坑,尤其经常使用的emojis字符。
注意这些方法中的index使用的是code unit值。

