Java编码utf8-字符、字符串类型

23
public class UTF8 {
    public static void main(String[] args){
        String s = "ヨ"; //0xFF6E
        System.out.println(s.getBytes().length);//length of the string
        System.out.println(s.charAt(0));//first character in the string
    }
}

输出:

3
ヨ
请帮助我理解这个问题。试图理解Java中如何使用UTF8编码。
根据Java文档定义的char:char数据类型是单个16位Unicode字符。
这是否意味着Java中的char类型只支持那些可以用2个字节表示且不超过2个字节的Unicode字符?
在上面的程序中,为该字符串分配的字节数为3,但第三行返回的第一个字符(在Java中为2个字节)可以容纳一个长度为3个字节的字符?我真的很困惑。
如有任何关于Java /通用概念的好参考资料,将不胜感激。

请参阅Unicode FAQ - McDowell
4个回答

35

您提供的代码示例中,没有直接使用 UTF-8。Java 字符串在内存中使用 UTF-16 进行编码。不适合单个 16 位字符的 Unicode 代码点将使用代理对(由两个字符构成)进行编码。

如果您未向 String.getBytes() 方法传递参数值,则该方法返回一个字节数组,其中包含使用底层操作系统默认字符集对字符串内容进行编码的结果。如果您想确保得到一个 UTF-8 编码的数组,则需要使用 getBytes("UTF-8") 方法。

调用 String.charAt() 方法只会从字符串的内存存储中返回原始的 UTF-16 编码字符。

因此,在您的示例中,Unicode 字符 使用两个 UTF-16 编码的字节 (即 0x6E 0xFF0xFF 0x6E,取决于大小端),存储在 String 的内存存储中。但是,在使用 getBytes() 方法生成的字节数组中,该字符使用三个字节进行编码,并且使用操作系统默认的字符集进行编码。

在 UTF-8 中,该特定 Unicode 字符也使用三个字节进行编码 (0xEF 0xBD 0xAE)。


我猜他的系统默认编码是UTF-8。 - Simone Gianni

4

String.getBytes()方法返回使用平台默认的字符编码的字节,该编码不一定与内部表示相匹配。

在大多数情况下,最好不要使用此方法,因为在大多数情况下,依赖于平台的默认编码是没有意义的。而应该使用String.getBytes(String charsetName)方法,显式指定用于将字符串编码为字节的字符集。


3
UTF-8是一种可变长度编码,对于ASCII字符(值在0到127之间)只使用一个字节,而对于其他Unicode符号则使用两个、三个或多个字节。
这是因为字节的高位用于告诉“这是一个多字节序列”,所以8位中的一位不用于实际表示“真实”数据(字符代码),而是用于标记字节。
因此,尽管Java在RAM中为每个字符使用2个字节,但当使用UTF-8对字符进行“序列化”时,它们可能会产生一个、两个或三个字节的结果字节数组,这就是UTF-8编码的工作原理。

6
UTF-8最多使用4个字节,而不是2个字节(如果考虑到UTF-8在修改之前超出了UTF-16支持的代码点,则可以使用6个字节)。 - Remy Lebeau
1
关于第三个陈述的问题:“尽管Java为每个字符在RAM中使用2个字节”。这是否意味着Java使用16位来表示Unicode的1,112,064个代码点?(2的16次方)不是比代码点的数量少吗?这个问题有效吗? - akd
@adosaiguas 请删除您的评论,该评论包含错误信息。 - Koray Tugay
@KorayTugay,从其他人的评论中可以清楚地看出我的初始评论是不正确的。但如果我删除它,其他人的评论就没有意义了,所以它还在那里。我认为删除一个两年前的评论没有意义...那我们应该删除这个答案中的所有评论吗? - adosaiguas
@KorayTugay 我们正在给这个答案添加无用的评论。在我看来,删除或编辑它会使一些其他评论失去意义,所以按照你的要求做毫无意义。 - adosaiguas
显示剩余4条评论

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接