Java字符编码核心是“内部统一、外部可配”:String在JVM内恒用UTF-16(Java 9+优化为byte[]+编码标识),char是无符号16位整数,仅能表示BMP字符,增补字符需代理对;与外部交互时必须显式指定Charset,否则依赖系统默认值易致不可逆乱码;IO操作须用InputStreamReader/OutputStreamWriter并传入StandardCharsets.UTF_8等明确编码,禁用FileReader/FileWriter。

Java 字符编码是面试中高频出现的考点,回答时要抓住三个关键层:底层表示(char/String 怎么存)、编码转换原理(为什么乱码、怎么转)、实际避坑点(跨平台、IO 流、JVM 参数)。下面分几个典型问题给出清晰、可落地的回答思路。
1. char 和 String 到底存的是什么?
char 不是“字符”,而是无符号 16 位整数(0–65535),对应 UTF-16 的一个代码单元。它只能表示基本多语言平面(BMP)内的 Unicode 字符,比如 'A'、'中';但像 emoji ?(U+1F60A)或生僻字 '?'(U+20BB7)这类增补字符,需要两个 char 组成代理对(surrogate pair)才能表示。
String 在 Java 8 及之前用 char[] 存储,在 Java 9+ 改为 byte[] + 编码标识(LATIN1 或 UTF-16),更省内存,但 length() 始终返回代码单元数,不是真实字符数。要获取真正 Unicode 字符个数,得用 str.codePointCount(0, str.length())。
2. 为什么中文在 Windows 上容易乱码?怎么解决?
因为 Windows 中文系统默认编码是 GBK,而 Linux/macOS 默认是 UTF-8。当一段用 GBK 写入的字符串,被 UTF-8 解码读取时,就会出现“?”或方块乱码——这不是显示问题,是解码错误导致的字节错配,且不可逆(例如 GBK 中无法表示的字符如 '',会变成 0x3F 0x3F,原始信息已丢失)。
- 避免硬依赖系统默认编码:显式指定编码,如
str.getBytes(StandardCharsets.UTF_8)或new String(bytes, StandardCharsets.UTF_8) - 文件读写用
InputStreamReader/OutputStreamWriter并传入 Charset - 启动 JVM 时加参数
-Dfile.encoding=UTF-8,统一环境基准(但不能替代代码层显式指定)
3. String.getBytes() 和 new String(byte[]) 为什么容易出错?
这两个方法都隐式使用系统默认编码,是乱码高发区。例如:
String s = "中"; byte[] bs = s.getBytes(); // 在 Windows 上是 GBK 编码的字节数组 String t = new String(bs); // 又用默认编码(GBK)解码 → 正常 String u = new String(bs, "UTF-8"); // 用 UTF-8 解码 GBK 字节 → 乱码
核心原则:编码和解码必须用同一字符集,否则必然出错。推荐始终显式传 Charset,不要依赖默认值。
4. IO 流中如何安全处理中文?
字节流(InputStream/OutputStream)不关心编码,只负责搬运字节;字符流(Reader/Writer)才负责编码转换。所以:
- 读文本文件:用
FileInputStream+InputStreamReader(StandardCharsets.UTF_8) - 写文本文件:用
FileOutputStream+OutputStreamWriter(StandardCharsets.UTF_8) - 网络通信(如 HTTP、Kafka 消息体):协议头或序列化方式通常约定编码,Java 客户端必须严格按约定 encode/decode
别用 FileReader/FileWriter——它们不接受 Charset 参数,永远用系统默认编码,极易翻车。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











