JDK 17 源码阅读 - 数据类型 - 04 - Character
JDK 17 源码阅读 - 数据类型 - 04 - Character
JDK 版本
以下代码均基于 JDK 17 版本。
Character 类是 char 的包装类,它提供了许多有用的方法来操作字符。
常量
Character 类中定义了很多常量,这也不难理解,毕竟世界上有很多种不同的语言和字符。
下面两个常量之前在字符串转数值时已经遇到过,分别表示自持的最小进制和最大进制。
// 数字和字符相互转换时所支持的最小进制
public static final int MIN_RADIX = 2;
// 数字和字符相互转换时所支持的最大进制
public static final int MAX_RADIX = 36;
下面这些常量是字符在 Unicode 规范中的通用分类。Unicode 规范中的通用分类主要是基于字符的语法和语义特性进行分类的。这些特性包括但不限于字符是否为大写字母、小写字母、数字、标点符号、控制字符等。
public static final byte UNASSIGNED = 0;
public static final byte UPPERCASE_LETTER = 1;
public static final byte LOWERCASE_LETTER = 2;
// ...
public static final byte INITIAL_QUOTE_PUNCTUATION = 29;
public static final byte FINAL_QUOTE_PUNCTUATION = 30;
下面这些常量是 Unicode 规范中的强双向字符类型,主要是根据字符的书写方向性进行分类的,在文本处理、排版和显示中具有重要作用。
public static final byte DIRECTIONALITY_UNDEFINED = -1;
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = 0;
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = 1;
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2;
// ...
public static final byte DIRECTIONALITY_FIRST_STRONG_ISOLATE = 21;
public static final byte DIRECTIONALITY_POP_DIRECTIONAL_ISOLATE = 22;
下面两个是最大值和最小值。从中也可以看出 char 型实例占用两个字节。
public static final char MIN_VALUE = '\u0000';
public static final char MAX_VALUE = '\uFFFF';
由于 char 最多占 2 个字节,所以最多表示 65536 个字符。在最初 Unicode 1.0 发布时,这个数量是足够的,但在后续加上世界上各种各样的语言后就完全不够了。
到这里需要介绍几个概念:代码单元、码点、代理码点(或者叫替代码点)。
码点是指在编码表中的某个字符对应的数字。对于 UTF-16 编码,码点的范围是 0x000000 到 0X10FFFF(对应 MIN_CODE_POINT 和 MAX_CODE_POINT 这两个常量),一共 1114112 个值。
在两个字节可以表示的范围内(\u0000 到 \uFFFF,即 BMP,基本多语言平面)的字符,每个字符用 16 位表示,通常被称为代码单元。而对于超出 \uFFFF 码点(MIN_SUPPLEMENTARY_CODE_POINT 常量就是来表示这个最小的码点)则采用一对连续的代码单元进行编码。
既然存在一个或两个代码单元表示一个码点的情况,那么如何区分这两种情况呢?
UTF-16 巧妙的利用了基本多语言平面中空闲的 2048 个值(U+D800 ~ U+DFFF,分别对应 MIN_SURROGATE 和 MAX_SURROGATE 这两个常量),这部分值通常被称为代理码点。U+D800 ~ U+DBFF 用于第一个代码单元(1024 个值),U+DC00 ~ U+DFFF 用于第二个代码单元(1024 个值)。一共可以表示最多 $2^{20} = 1048576$ 个码点,再加上基本多语言平面度的 65536 个字符,一共是 1114112 个字符。
public static final char MIN_HIGH_SURROGATE = '\uD800';
public static final char MAX_HIGH_SURROGATE = '\uDBFF';
public static final char MIN_LOW_SURROGATE = '\uDC00';
public static final char MAX_LOW_SURROGATE = '\uDFFF';
public static final char MIN_SURROGATE = MIN_HIGH_SURROGATE;
public static final char MAX_SURROGATE = MAX_LOW_SURROGATE;
public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000;
public static final int MIN_CODE_POINT = 0x000000;
public static final int MAX_CODE_POINT = 0X10FFFF;
这里以 Character.MIN_SUPPLEMENTARY_CODE_POINT 码点为例,查看其对应的代码单元的值:
Integer.toHexString(Character.toChars(Character.MIN_SUPPLEMENTARY_CODE_POINT)[0]); // d800
Integer.toHexString(Character.toChars(Character.MIN_SUPPLEMENTARY_CODE_POINT)[1]); // dc00
顺便说一下 UTF-8 编码,它也是一种变长编码,可以使用 1 - 4 个字节(也有说最多 6 个字节的,不太清楚什么情况下会有 6 个字节)表示一个字符。其编码规则类似,只是用的代理区域不同。
Subset & UnicodeBlock
Subset 类的实例表示 Unicode 字符集的特定子集。在 Character 类中定义的唯一子集系列是 Character.UnicodeBlock。
在 UnicodeBlock 类中定义了 Unicode 规范中的所有子集的名称以及对应的码点范围。