在 Java 内部,字符串被实现为 UTF-16 代码单元的数组。但这是一个实现细节,有可能实现一个在内部使用不同编码的 JVM。
(注意“encoding”、“charset”和 Charset 或多或少是同义词。)
应将字符串视为 Unicode 代码点序列(即使在 Java 中它是 UTF-16 代码单元序列)。
如果您的 Java 程序中有字符串,则不正确说它是“UTF-8 字符串”或“以 UTF-8 编码的字符串”。这没有任何意义,除非您谈论的是无关紧要的内部表示。
如果您使用编码(例如 UTF-8 或 Shift-JIS)将其解码为字符串,则您可以拥有解码为字符串的字节序列。
如果您使用编码(例如 UTF-8 或 Shift-JIS)对字符串进行编码,您也可以将字符串编码为字节序列。
简而言之,编码或字符集是一对两个函数,“编码”和“解码”,这样:
// String -> encode -> bytes
byte[] bytes = string.getBytes(encoding);
// or using Charset
ByteBuffer byteBuffer = charset.encode(string);
// bytes -> decode -> String
String string = new String(bytes, encoding);
// or using Charset
String string = charset.decode(byteBuffer).toString();
因此,如果您有一个使用 UTF-8 编码的 byte[]:
byte[] utf8Bytes = "ピーター・ジョーズ".getBytes("UTF-8");
// utf8Bytes now contains, in hexadecimal
// e3 83 94 e3 83 bc e3 82 bf (ピ ー タ)
// e3 83 bc e3 83 bb e3 82 b8 (ー ・ ジ)
// e3 83 a7 e3 83 bc e3 82 ba (ョ ー ズ)
你可以使用这些字节创建一个字符串:
String string = new String(utf8Bytes, "UTF-8");
// String now contains "ピーター・ジョーズ"
然后您可以使用以下方法将该字符串编码为 Shift-JIS:
byte[] shiftJisBytes = string.getBytes("Shift-JIS");
// shiftJisBytes now contains, in hexadecimal
// 83 73 81 5b 83 5e (ピ ー タ)
// 81 5b 81 45 83 57 (ー ・ ジ)
// 83 87 81 5b 83 59 (ョ ー ズ)
由于这些字节表示使用Shift-JIS 编码的字符串,因此尝试使用UTF-8 解码会产生垃圾:
String garbage = new String(shiftJisBytes, "UTF-8")
// String now contains "�s�[�^�[�E�W���[�Y"
// � is the character decoded when given an invalid UTF-8 sequence
// 83 73 81 5b 83 5e (� s � [ � ^)
// 81 5b 81 45 83 57 (� [ � E � W)
// 83 87 81 5b 83 59 (� � � [ � Y)
此外,请记住,如果您将字符串打印到输出,例如System.out,它将使用系统默认编码,该编码取决于系统将字符串转换为字节。看起来你的系统默认是UTF-8。
System.out.print(string);
// equivalent to:
System.out.write(string.getBytes(Charset.defaultCharset()));
如果您的输出是例如 Windows 控制台,它会在将这些字节转换为字符串之前,使用很可能完全不同的编码(可能是 CP437 或 CP850)将其呈现给您。
这最后一部分可能会绊倒你。