OpenJDK 18起默认字符编码强制为UTF-8(JEP 400),中文乱码多因源码、编译、运行三阶段编码不一致所致,需分层排查:源码文件编码、JVM file.encoding、终端解码设置及第三方库配置。

OpenJDK 18 起默认字符编码已强制设为 UTF-8,不是“可选优化”,而是 JVM 规范级行为变更(JEP 400)。你升级到 OpenJDK 25 后控制台或日志出现中文乱码,大概率不是配置漏了,而是源码、编译、运行三阶段的编码链断在了某一处。
Charset.defaultCharset() 返回 UTF-8,但 System.out.println("中文") 还是乱码
这说明乱码发生在**输出解码环节**,而非字符串构造阶段。JVM 把 "中文" 按 UTF-8 编码成字节写入 System.out,但终端(如 IDEA 控制台)用别的编码(比如 GBK)去解码这些字节,自然错位。
- 检查 IDEA 的控制台编码:Settings → Editor → File Encodings → **Console Encoding**,必须和 JVM 实际使用的编码一致
- 确认 JVM 启动时未被覆盖:
file.encoding系统属性优先级高于默认值,运行java -XshowSettings:properties -version 2>&1 | grep file.encoding查看真实生效值 - Windows 下若用 CMD/PowerShell 直接运行 class 文件,还需确认终端代码页:
chcp命令输出应为65001(UTF-8),否则即使 JVM 用 UTF-8,终端也解不开
源码文件是 GBK 编码,升级后编译出的 class 输出乱码
这是最典型的“静默破坏”:JDK 17 及之前按系统默认(Windows 中文版为 GBK)读源码;JDK 25 默认用 UTF-8 读,而你的 .java 文件实际是 GBK 存的,导致字符串字面量在编译期就被误读成乱码,运行时再怎么设置都救不回来。
- 用编辑器(如 IDEA)打开源文件,右下角查看当前文件编码,如果不是
UTF-8,先转码保存(注意备份) - 强制编译器按正确编码读源码:加
-encoding GBK参数(仅临时过渡),例如:javac -encoding GBK Main.java - 长期方案:统一项目所有源文件为
UTF-8,并在pom.xml(Maven)或build.gradle(Gradle)中显式声明源码编码,避免依赖 JDK 默认
第三方库(如 Log4j、Jackson)输出中文异常
这些库大多遵循 JVM 默认编码,但部分老版本会绕过 file.encoding,直接查操作系统 locale。升级后它们可能仍试图用 GBK 解码字节流,与 JVM 的 UTF-8 输出冲突。
- Log4j2:检查
log4j2.xml中PatternLayout是否含charset="UTF-8"属性;异步 Appender 尤其容易忽略此配置 - Jackson:
ObjectMapper默认使用UTF-8,但若手动构建JsonGenerator并传入OutputStream,需确保该流未被包装成其他编码(如PrintWriter(out, false)会用平台默认编码) - Spring Boot:在
application.properties加server.servlet.encoding.charset=UTF-8和server.servlet.encoding.force=true,但注意这只是 HTTP 层,不影响日志或控制台
真正难处理的不是“哪里改”,而是“哪一层出的问题”——编译期、JVM 启动期、终端解码期、还是库内部硬编码。别一上来就全局加 -Dfile.encoding=UTF-8,先用 Charset.defaultCharset() 和 chcp / locale 定位真实断点。很多所谓“乱码”,其实是三处编码各执一词,强行统一反而掩盖了根本矛盾。


















