Unicode字符集
Unicode,是计算机科学领域里的一项业界标准。为每种语言中的每个字符设定了统一并且唯一的整数值,把这个数值称为码点(Code Point)。
但是它并不规定计算机如何存储和传输这个数值(以多少个字节存储,是定长还是变长,Unicode没有字节长度的概念)。
Unicode 定义了一个码点空间包含1,114,112个码点,范围从0到0x10FFFF,也就是说,它仅仅是一个字符映射集。其中 0x0000 ~ 0xFFFF 的字符表示常用字符集,称 BMP字符,0x10000 ~ 0x10FFFF 字符叫增补字符。
Unicode 目前规划的总空间是17个平面(平面0至16),0x0000 至 0x10FFFF,每个平面有 65536 个码点。
例如,"AB中国"这个字符串,对应的 Unicode 编码为:
A -> \u0041
B -> \u0042
中 -> \u0049
国 -> \u56FD
Unicode的实现
同一 Unicode 值可以被编码成不同的二进制表示,以便在存储和网络上传输。Unicode的实现:UTF-8、UTF-16、UTF-32、UCS-2等。
UTF-8, Unicode Transformation Format – 8-bit
- 使用 1 ~ 4 个字节变长编码表示「1,112,064」个 Unicode 码点
- 兼容 ASCII
- 码点数值越小,使用的字节数越少,出现的频率越高
UTF-16
- 使用 1 ~ 2 个 16bit 变长编码表示「1,112,064」个 Unicode 码点
- 它扩展于固定16bit长度的UCS-2
UTF-32
- 4 字节定长编码
- 其高位均为0,空间浪费比较严重,因此应用很少
Java 使用何种编码
Java char 占 2 个字节,使用 UTF-16BE 表示一个字符。由于它只使用 2 个字节,所以 char 只用能表示部分 UTF-16 编码,即 0x0000 ~ 0xFFFF。对于 4 字节的 UTF-16,需要使用 2 个 char。
String 始终是按 UTF-16BE 处理字符的,对 BMP 字符,使用一个 char,两个字节;对于增补字符,使用两个 char,四个字节。以上是由 Java language spec 规定的。
Java 源文件默认采用平台编码存储,Linux/Unix 是 UTF-8,Window 采用 GBK。一般常用的 IDE 如 Intellij IDEA 和 Eclipse 都能提供多种编码。编译器 javac 读取 Java 源文件时,如果不显示指定 -encoding,则默认以 Charset.defaultCharset() 读取,如果与源文件的编码不同,则会报错。
javac -encoding gbk/utf-8 仅告诉编译器以何种编码读取 Java 源文件,然后编译器在编译时就用指定的编码把源文件的字符串转化为UTF-8字符串。
不管 Java 源文件是什么编码,Java class 字节码文件中字符串字面量始终是 UTF-8 编码,这是由 JVM spec 规定。
UTF-8 和 GBK2312 编码的 Java 源文件,分别编译成 class 文件,两者相同。
System.out、PrintStream 将会在输出之前,把字符串从 UTF-16 转换成默认系统编码(JVM 从操作系统读取)。
Java 中把 String 转换成字节码,默认使用平台默认的编码。看看 getBytes 的注释。
/**
* Encodes this {@code String} into a sequence of bytes using the
* platform's default charset, storing the result into a new byte array.
*
* <p> The behavior of this method when this string cannot be encoded in
* the default charset is unspecified. The {@link
* java.nio.charset.CharsetEncoder} class should be used when more control
* over the encoding process is required.
*
* @return The resultant byte array
*
* @since JDK1.1
*/
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}