众所周知,在 UTF-8
编码中,中文字符通常占用 3 个字节:
import java.nio.charset.StandardCharsets;
/**
* @author shenyang
* @version 1.0
* @info untitled
* @since 2024/6/30 上午9:42
*/
public class Test {
public static void main(String[] args) {
String str = "啊";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
int byteCount = bytes.length; // 字节数
System.out.println(byteCount);
}
}
输出确实符合这个结论。
而在 UTF-16
编码中,中文字符通常占用 2 个字节:
import java.nio.charset.StandardCharsets;
/**
* @author shenyang
* @version 1.0
* @info untitled
* @since 2024/6/30 上午9:42
*/
public class Test {
public static void main(String[] args) {
String str = "啊";
byte[] bytes = str.getBytes(StandardCharsets.UTF_16);
int byteCount = bytes.length; // 字节数
System.out.println(byteCount);
}
}
但是运行结果却为 4:
这是位什么呢?经查阅资料发现:
- 在
UTF-16
编码中,每个字符通常占用 2 个字节,但在字符串转换为字节数组时,UTF-16 会在字节数组的开头加入一个字节顺序标记(BOM
),这会额外占用 2 个字节。 - 因此,
UTF-16
编码的结果包含 2 个字节的字节顺序标记和 2 个字节的实际字符编码,总共是 4 个字节。
将字符串修改为两个中文字符。
String str = "啊啊";
相比于原来确实是多出两个字节,证实了结论。
但是现在还有一个疑问,这个字节顺序标记到底是个什么东西呢?
它是一种用于指示文本文件或数据流中字节顺序(即字节序)的方法。它在某些编码(如UTF-16和UTF-32)中用于标识字节顺序是大端序(Big Endian)还是小端序(Little Endian)。具体来说:
- 大端序(Big Endian): 高字节(最重要的字节)存储在内存的低地址端。
- 小端序(Little Endian): 低字节(最重要的字节)存储在内存的低地址端。
在UTF-16编码中,BOM的值是 0xFEFF
:
- 如果文件的前两个字节是
0xFE 0xFF
,表示文件使用大端序。 - 如果文件的前两个字节是
0xFF 0xFE
,表示文件使用小端序。
当你将字符串转换为UTF-16
编码的字节数组时,Java通常会在字节数组的开头添加BOM,以指示字节顺序。这样,接收该字节数组的程序或设备可以根据BOM正确解码字节数据。