Java 字符串在编码和解码后不同

Java 字符串在编码和解码后不同,java,string,unicode,character-encoding,unicode-literals,Java,String,Unicode,Character Encoding,Unicode Literals,我偶然发现了编码/解码字符串的奇怪行为。请看一个示例: @Test public void testEncoding() { String str = "\uDD71"; // {56689} byte[] utf16 = str.getBytes(StandardCharsets.UTF_16); // {-2, -1, -1, -3} String utf16String = new String(utf16, StandardCharsets.UTF_16); //

我偶然发现了编码/解码字符串的奇怪行为。请看一个示例:

@Test
public void testEncoding() {
    String str = "\uDD71"; // {56689}
    byte[] utf16 = str.getBytes(StandardCharsets.UTF_16); // {-2, -1, -1, -3}
    String utf16String = new String(utf16, StandardCharsets.UTF_16); // {65533}
    assertEquals(str, utf16String);
}
我认为这项测试会通过,但事实并非如此。有人能解释为什么编码和解码的字符串不等于原始字符串吗?

不是有效的码点,因为U+D800..U+DFFF由Unicode保留,以免与UTF-16混淆。因此,这些代码点不应显示为有效的字符数据。根据Unicode标准:

孤立的代理代码点没有解释;因此,不提供字符代码表或名称列表 这个范围

不过,这是可行的:

@Test
public void testEncoding() {
    String str = "\u0040";
    byte[] utf16 = str.getBytes(StandardCharsets.UTF_16);
    String utf16String = new String(utf16, StandardCharsets.UTF_16);
    assertEquals(str, utf16String);
}

因此,不是您的代码有错,而是您试图使用无效的代码点。

您必须至少显示实际输出您希望看到的输出?字节数组?UTF16字符串?以什么形式?我认为你自己可以很容易地进行这个测试。一般来说,输出结果是这些字符串是不同的
\uDD71
是低代理。单独使用它是无用的,并且不表示任何代码点。因此,它被替换为
\uFFFD
。如果您喜欢异常而不是替换,请避免使用字符串构造函数。我已经接受了您的答案,但非常感谢@Johannes Kuhn,他是第一个帮助我理解问题的人。是的,我在完成回答后看到了他的评论。他比我更了解这个主题,但这是谷歌搜索告诉我的。