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

2022-09-03 08:48:36

我已经阅读了以下帖子:

  1. Java对String的内部表示是什么?修改后的 UTF-8?UTF-16?
  2. https://docs.oracle.com/javase/8/docs/api/java/lang/String.html

现在考虑下面给出的代码:

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("----------------------------------------------------------------------------------------");
}

当我尝试调试上面代码中的行时,调试器将我带入了 String 类的方法,然后进入了 StringCoding 类的方法。编码方法 () 的第一行在调试期间返回“UTF-8”作为默认编码。我以为它是“UTF-16”。character.getBytes()getBytes()static byte[] encode(char[] ca, int off, int len)String csn = Charset.defaultCharset().name();

该程序的输出为:

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


答案 1

角色是一个图形实体,是人类文化的一部分。当计算机需要处理文本时,它会使用这些字符的表示形式(以字节为单位)。使用的确切表示形式称为编码

有许多编码可以表示相同的字符 - 通过 Unicode 字符集,或通过其他字符集(如各种 ISO-8859 编码或 JIS X 0208)。

在内部,Java使用UTF-16。这意味着每个字符都可以由一个或两个两个字节的序列表示。您使用的字符“最”具有码位 U+6700,该码位在 UTF-16 中表示为字节0x67,字节0x00。

这就是内部编码。除非您转储内存并查看转储图像中的字节,否则您无法看到它。

但该方法返回此内部表示形式。它的文档说:getBytes()

public byte[] getBytes()

使用平台的默认字符集将其编码为字节序列,并将结果存储到新的字节数组中。String

“平台的默认字符集”是您的区域设置变量所说的。那是。因此,它采用 UTF-16 内部表示形式,并将其转换为不同的表示形式 - UTF-8。UTF-8

请注意,

new String(bytes, StandardCharsets.UTF_16);

不会像您假设的那样“将其显式转换为 UTF-16”。此字符串构造函数采用一个字节序列,该序列应位于您在第二个参数中给出的编码中,并将其转换为这些字节在该编码中表示的任何字符的 UTF-16 表示形式。

但是您已经为它提供了一个以 UTF-8 编码的字节序列,并告诉它将其解释为 UTF-16。这是错误的,并且您没有获得预期的字符或字节。

你不能告诉Java如何在内部存储字符串。它始终将它们存储为 UTF-16。构造函数告诉 Java 从应该在给定字符集中的字节数组创建 UTF-16 字符串。该方法告诉 Java 为您提供一个字节序列,这些字节表示给定编码(字符集)中的字符串。没有参数的方法也执行相同的操作 - 但使用平台的默认字符集进行转换。String(byte[],Charset)getBytes(Charset)getBytes()

所以你误解了给你的东西。它不是内部表示。你不能直接得到它。只有会给你这个,只是因为你知道这是Java中的内部表示。如果未来版本的Java决定用不同的编码来表示字符,则不会向您显示内部表示。getBytes()getBytes(StandardCharsets.UTF_16)UTF-16getBytes(StandardCharsets.UTF_16)

编辑:事实上,Java 9 在字符串的内部表示中引入了这样的更改,默认情况下,字符全部属于 ISO-8859-1 范围的字符串在 ISO-8859-1 内部表示,而至少有一个字符超出该范围的字符串在内部表示,就像以前一样。因此,确实,不再返回内部表示。getBytes(StandardCharsets.UTF_16)


答案 2

如上所述,java使用UTF-16作为字符数据的编码。

可以补充一点,可表示字符集被限制为整个Unicode字符集的适当子集。(我相信java将其字符集限制为Unicode BMP,所有这些都适合UTF-16的两个字节。

因此,应用的编码确实是 UTF-16,但应用它的字符集是整个 Unicode 字符集的适当子集,这保证了 Java 在其内部 String 编码中始终为每个令牌使用两个字节。


推荐