我已经阅读了以下帖子:
现在考虑下面给出的代码:
public static void main(String[] args) {
printCharacterDetails("最");
}
public static void printCharacterDetails(String character){
System.out.println("Unicode Value for "+character+"="+Integer.toHexString(character.codePointAt(0)));
byte[] bytes = character.getBytes();
System.out.println("The UTF-8 Character="+character+" | Default: Number of Bytes="+bytes.length);
String stringUTF16 = new String(bytes, StandardCharsets.UTF_16);
System.out.println("The corresponding UTF-16 Character="+stringUTF16+" | UTF-16: Number of Bytes="+stringUTF16.getBytes().length);
System.out.println("----------------------------------------------------------------------------------------");
}
当我尝试在上面的代码中调试行character.getBytes()
时,调试器将我带入String类的getBytes()
方法,然后进入StringCoding类的static byte[] encode(char[] ca, int off, int len)
方法。编码方法的第一行(String csn = Charset.defaultCharset().name();
)返回" UTF-8"作为调试期间的默认编码。我预计它会是" UTF-16"。
该计划的输出是:
最大值= 6700的Unicode值 UTF-8字符=最|默认值:字节数= 3
相应的UTF-16字符=�| UTF-16:字节数= 6
当我在程序中明确地将其转换为UTF-16时,花费了6个字节来表示该字符。对于UTF-16,它不应该使用2或4个字节吗?为什么使用6个字节?
我的理解在哪里出错了? 我使用Ubuntu 14.04,locale命令显示以下内容:
LANG=en_US.UTF-8
这是否意味着JVM决定在底层操作系统的基础上使用哪种编码,还是只使用UTF-16? 请帮我理解这个概念。
答案 0 :(得分:12)
角色是图形实体,是人类文化的一部分。当计算机需要处理文本时,它使用这些字符的表示(以字节为单位)。使用的确切表示称为编码。
有许多编码可以表示相同的字符 - 通过Unicode字符集,或通过其他字符集,如各种ISO-8859编码,或JIS X 0208。
在内部,Java使用UTF-16。这意味着每个字符可以由两个字节的一个或两个序列表示。您使用的字符,最大,代码点为U + 6700,以UTF-16表示为字节0x67和字节0x00。
内部编码。除非你转储内存并查看转储图像中的字节,否则你无法看到它。
但方法getBytes()
不会返回此内部表示。它的文件说:
public byte[] getBytes()
将此
String
编码为字节序列 使用平台的默认字符集,将结果存储为新的 字节数组。
"平台的默认字符集"是你的语言环境变量所说的。也就是UTF-8
。因此它采用UTF-16内部表示,并将其转换为不同的表示形式 - UTF-8。
请注意
new String(bytes, StandardCharsets.UTF_16);
不"明确地将其转换为UTF-16"正如你所想的那样。这个字符串构造函数接受一个字节序列,它应该是您在第二个参数中给出的编码,并将其转换为该字节在该编码中表示的任何字符的UTF-16表示。
但是你已经给它一个以UTF-8编码的字节序列,并告诉它将其解释为UTF-16。这是错误的,你没有得到你期望的字符 - 或字节 -
您无法告诉Java如何在内部存储字符串。它总是将它们存储为UTF-16。构造函数String(byte[],Charset)
告诉Java从应该在给定字符集中的字节数组创建UTF-16字符串。方法getBytes(Charset)
告诉Java为您提供一个字节序列,表示给定编码(charset)中的字符串。没有参数的方法getBytes()
也是如此 - 但是使用平台的默认字符集进行转换。
所以你误解了getBytes()
给你的东西。它的不是内部表示。你不能直接得到它。只有getBytes(StandardCharsets.UTF_16)
会给你这个,而且只是因为你知道UTF-16
是Java中的内部表示。如果Java的未来版本决定以不同的编码表示字符,那么getBytes(StandardCharsets.UTF_16)
将不会向您显示内部表示。
编辑:实际上,Java 9引入了字符串内部表示的这种变化,默认情况下,字符全部属于ISO-8859-1范围的字符串在内部表示为ISO-8859-1,而具有该范围之外的至少一个字符的字符串在内部以UTF-16表示,如前所述。确实,getBytes(StandardCharsets.UTF_16)
不再返回内部表示。
答案 1 :(得分:2)
如上所述,java使用UTF-16作为字符数据的编码。
可以添加的是,可表示字符集限于整个Unicode字符集的适当子集。 (我相信java将其字符集限制为Unicode BMP,所有这些都适合UTF-16的两个字节。)
因此应用的编码确实是UTF-16,但应用它的字符集是整个Unicode字符集的适当子集,这保证了Java在其内部字符串编码中始终使用每个标记两个字节。