1、java中的char的大小是范围是0到0xffff,能表示绝大多数的中文。
/*** The constant value of this field is the smallest value of type* {@code char}, {@code '\u005Cu0000'}.** @since 1.0.2*/public static final char MIN_VALUE = '\u0000';/*** The constant value of this field is the largest value of type* {@code char}, {@code '\u005CuFFFF'}.** @since 1.0.2*/public static final char MAX_VALUE = '\uFFFF';
一个char能表示的范围的字符称为:Basic Multilingual Plane(BMP),Java设计的初始,大家天真地认为两个字节大小(65536)已经能表示完世界上所有要用到的符号,所以java在给自己设计char类型的时候,用了两个字节(unsigned short)定义char(一开始的UCS-2,后来的UTF-16),UTF-16编码方式:字符编码的概念(UTF-8、UTF-16、UTF-32都是什么鬼)_顾小暖的博客-CSDN博客。
2、java对应utf-16演变的修改
java设计者当时是基于UCS-2固定两个字节能表示所有unicode字符来设计的char类型,既然UCS-2演化为了utf-16,由定长变为不定长,所以也得跟着调整,当时char类型已经是固定两个字节了,所以char就是固定两个字节,因此如今也只能表示BMP范畴的字符,当然这包括了BMP范畴内的那些无效码位,也就是char能够表示high/low surrougate。
这感觉就像开发者已经实现了预期的功能,然后产品经理跑过来说需求变更了...
官方关于如何支持non-BMP的说明:
www.oracle.com/technical-resources/articles/javase/supplementary.html
单个char无法表示non-BMP字符,但是两个char组合一起就可以。
这也是为什么单个char类型表示non-BMP字符编译不过,而String可以的原因(java9之前String是使用char[]实现,后面是byte[]实现)。
3、code point和code unit
/*** 输出* 龘* 知乎-发现更大的世界,😂* 30693 20046 45 21457 29616 26356 22823 30340 19990 30028 65292 128514* code unit size:13* code point size:12*/@Testpublic void testChar1() {char ch = '龘';System.out.println(ch);// for (int i = 0; i <= Integer.MAX_VALUE; i++) {
// char c = (char) i;
// System.out.print(c);
// System.out.println("=" + i);
// }//对non-MBP的字符,使用两个char来保存final String content = "知乎-发现更大的世界,\uD83D\uDE02";for (int i = 0; i < content.length(); i++) {char c = content.charAt(i);System.out.print(c);}System.out.println();for (int i = 0; i < content.length(); i++) {char c = content.charAt(i);System.out.print(c + " ");}System.out.println();for (int j = 0; j < content.codePointCount(0, content.length()); j++) {int value = content.codePointAt(j);System.out.print(value + " ");}System.out.println();//char 数组的大小是13个System.out.println("code unit size:" + content.length());//表示的字符只有12个System.out.println("code point size:" + content.codePointCount(0, content.length()));}
java.lang.String#codePointAt这个方法要注意参数index的索引是unit size的索引,不是point size的索引,所以如果你有如下代码:
@Testpublic void testChar2() {String content = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD842\uDCB8\uD841\uDF96";System.out.println(content);for (int j = 0; j < 5; j++) {//需要每次读取两个才能得到正确的Unicode编码int value = content.codePointAt(j * 2);System.out.print(value + " ");}}