绝大多数文件级中文乱码根源在于InputStreamReader和OutputStreamWriter未显式指定编码,而FileReader/FileWriter隐式使用平台默认编码,导致与文件真实编码不一致。

查 InputStreamReader 和 OutputStreamWriter 是否显式指定了编码
绝大多数文件级中文乱码,根源就在这里。Java 的 FileReader 和 FileWriter 会隐式使用平台默认编码(Linux 常为 UTF-8,Windows 常为 GBK),一旦与文件真实编码不一致,立刻出问题。
常见错误写法:
new FileReader("data.txt") // ❌ 隐式用系统默认编码
new FileWriter("out.txt") // ❌ 同上
正确做法必须显式传入 Charset:
- 优先用
StandardCharsets.UTF_8(JDK 7+)或字符串"UTF-8" - 如果明确知道是 GBK 文件(如老系统导出),则用
StandardCharsets.GBK或"GBK" - 不要依赖
System.getProperty("file.encoding"),它不可靠且易被 JVM 参数覆盖
示例:
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8))) {
// ...
}
看数据库连接串里有没有 characterEncoding 和 useUnicode
MySQL 是重灾区。即使数据库本身建表用了 utf8mb4,只要 JDBC 连接没配对,Java 读写照样乱码。
典型缺失配置:
- 漏掉
useUnicode=true:JDBC 不启用 Unicode 模式,强制走 Latin-1 路径 - 漏掉
characterEncoding=UTF-8:JDBC 不知道该用什么编码解码字节流 - URL 中混用
&和&:XML/HTML 场景下未转义导致参数截断,characterEncoding实际没生效
正确连接 URL 示例:
jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
注意:characterEncoding 是客户端连接参数,和数据库服务端的 collation_server 无关;两者不一致时,以连接参数为准。
检查 JVM 启动参数是否设了 -Dfile.encoding
这个参数会影响所有未显式指定编码的 I/O 操作,比如 System.out.println()、new Scanner(System.in)、甚至某些日志框架的默认输出。
但它不是万能解药:
- 只对“未指定编码”的操作生效;一旦代码里写了
new String(bytes, "GBK"),它就完全无视-Dfile.encoding - 不同 JDK 版本行为有差异:OpenJDK 17+ 对该参数更严格,而旧版可能被系统 locale 覆盖
- Docker 容器中尤其危险——Alpine 镜像默认 locale 是
C,-Dfile.encoding=UTF-8可能被忽略
建议只在无法修改代码的遗留场景下临时加,生产环境应优先修复代码层编码声明。
确认控制台或日志输出终端能否显示中文
乱码不一定来自 Java 程序本身,也可能是终端“看不懂”。现象是:程序内 String 打印出来是正常中文,但终端显示为 ??? 或方框。
排查要点:
- Linux 终端:运行
locale,确保LANG包含UTF-8(如zh_CN.UTF-8或en_US.UTF-8);若为C或空,需设置export LANG=en_US.UTF-8 - Docker 容器:基础镜像(尤其是 Alpine)默认无中文 locale,需在 Dockerfile 中安装并生成 locale,例如
apk add --no-cache tzdata && cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime并配合ENV LANG=C.UTF-8 - IDE 控制台(IntelliJ/Eclipse):检查
Settings > Editor > File Encodings中的 “Console Encoding” 是否与项目编码一致
一个快速验证法:在终端直接执行 echo "中文" | hexdump -C,如果输出是 e4 b8 ad(UTF-8 编码),说明终端本身支持;否则问题在终端配置。
最常被忽略的是:你以为在修 Java 编码,其实只是终端没准备好。先确认字节流到了哪一层再动手,比盲目加 -Dfile.encoding 有效得多。


















