Java使用UTF-8或UTF-16编码哪种编码?

时间:2016-10-10 09:26:01

标签: java encoding utf-8 default utf-16

我已经阅读了以下帖子:

  1. What is the Java's internal represention for String? Modified UTF-8? UTF-16?
  2. https://docs.oracle.com/javase/8/docs/api/java/lang/String.html
  3. 现在考虑下面给出的代码:

    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? 请帮我理解这个概念。

2 个答案:

答案 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在其内部字符串编码中始终使用每个标记两个字节。