要搞清楚Java字符(串)编码,先得区分清楚内码(internal encoding)和外码(external encoding)
- 内码是程序内部使用的字符编码,特别是某种语言实现其char或String类型在内存里用的内部编码
- 外码是程序与外部交互时外部使用的字符编码
Java使用UTF-16作为字符的内码
然而UTF-16在Java设计之初还是定长编码,后来Unicode涵盖的字符变多了之后,UTF-16变成了两字节或者四字节的变长编码
Java也只好跟进,为了实现UTF-16的变长编码语义,Java规定:
- 一个完整的字符是一个code point
- 一个code point可以对应 1 到 2 个code unit
- 一个code unit是固定的两字节
备注:本文中提到的关于:UTF-16编码,Unicode,BOM等信息,不是很清楚的同学可以查看我的另一篇文章 字符集和字符编码
char
对应上述的一个code unit,固定为两个字节,也可以理解成是一个两字节的无符号整型
只有只需1个code unit的code point才可以完整的存在char里,所以char类型不一定能表示一个UTF-16的字符
unicode
char是固定的两个字节,所以在java中使用unicode编码只能这样:
- 一个char ‘\uXXXX’
- 两个char(String) “\uXXXX\uXXXX”
1 | char u = '\u4E2D'; // 中 |
String
String作为char的序列,对于两字节的UTF-16只需要用一个char表示,对于需要2个code unit(四字节)的UTF-16可以包含由两个char组成的 “surrogate pair” 来表示
为此Java的标准库新加了一套用于访问code point的API,而这套API就表现出了UTF-16的变长特性, 包括String, StringBuffer, StringBuilder等
getBytes
上面说了这么多内码,下面说说我们程序与外部交互时更加常用用的外码
String.getBytes()是一个用于将String的内码转换为指定的外码的方法:
- 无参数版使用平台的默认编码作为外码
- 有参数版使用参数指定的编码作为外码
将String的内容用外码编码后,结果放在一个新byte[]返回
CompressedString
由于Java使用UTF-16作为内码,只有2和4字节两种情况降低了复杂的,然而很多时候我们用到的字符使用一个字节的ASCII就可以表示,使用两个字节无疑增加了开销
所以在Sun JDK6中有一个压缩字符串(-XX:+UseCompressedString)的功能。启用后,String内部存储字符串内容可能用char[],也可能用byte[];
当整个字符串所有字符都在ASCII编码范围内时,就使用byte[](ASCII序列)来存储,此时字符串就处于压缩状态
然而压缩字符串这个功能的实现并不够理想,实现的太复杂而且效果未如预期的好,所以后续JDK版本中并没有包含,后续版本可能改进后继续加入该功能
实例
1 | String str = "z中"; |