适用读者:控制台出现"锟斤拷""????"、配置文件中文变方块、日志里中文成乱码的人。
前置:S01-B04(File Encodings 设置)。
实测环境:JDK 21.0.12.1 / Ubuntu 24.04.1(编码属性与 GBK 错配为 2026-11-05 实测);Windows 部分为文档口径。
覆盖旧文:A-1377(IDEA 控制台中文乱码 4 种解决方案,35 点)。
新增扩展:E-S01-021(file.encoding与 JDK 18 默认 UTF-8)、E-S01-022(Windows 代码页与 locale)。
上一篇:S01-J05 · IDE 性能与大工程:内存、索引、插件成本
下一篇:S01-J07 · 依赖治理。
1. 一句话结论
乱码从来不是"某处设错了",而是"写入用的编码 ≠ 读取用的编码"。 所以排障只有一件事:确定字节是按什么编码写的,再确定是谁按什么编码读的。
四层,各管一段:
① 源文件字节 ──► ② 编译期(javac 读写源文件)──► ③ 运行时(JVM 默认编码)──► ④ 显示端(IDE 控制台 / 终端)
存成 UTF-8? -encoding/-Dfile.encoding file.encoding/stdout.encoding chcp / locale / 字体
改了某一层还在乱码,说明问题在另一层。 这是旧文"4 种方案挨个试"的根本缺陷——没有定位,只能穷举。
2. 先把"当前是什么编码"查清楚(实测)
$ java -XshowSettings:properties -version 2>&1 | grep -E "file.encoding|native.encoding|sun.jnu.encoding|stdout.encoding"
file.encoding = UTF-8
native.encoding = UTF-8
stdout.encoding = UTF-8
sun.jnu.encoding = UTF-8
| 属性 | 含义 | 谁在用 |
|---|---|---|
file.encoding |
JVM 默认字符集(读写文件、new String(bytes) 的默认) |
你的代码、老依赖 |
native.encoding |
操作系统本地编码 | JDK 内部、部分 native 库 |
stdout.encoding |
标准输出的编码 | System.out 打日志到控制台 |
sun.jnu.encoding |
文件名/路径编码 | 读写中文文件名 |
JDK 18 起 file.encoding 默认就是 UTF-8(JEP 400),所以很多老项目"以前要加 -Dfile.encoding=UTF-8,现在不加也对"。但也因此,在 Windows 上跑老代码时会出现"以前没事、现在乱了"——因为以前默认是 GBK(当前代码页),现在默认 UTF-8。
旧文勘误:A-1377 把"修改 IDEA 配置文件加
-Dfile.encoding=UTF-8"作为一条通用方案。在 JDK 18+ 上这条通常已无必要;真正需要它的场景是"JDK 8/11 + Windows GBK 代码页"。版本不同,答案不同——这条差异旧文没有标注。
3. 实验:同一份字节,换编码读会怎样(实测)
# 把"中文"按 GBK 编码存成字节
$ printf "中文" | iconv -f UTF-8 -t GBK > gbk.bin
# 按 UTF-8 去读(错配)
$ iconv -f UTF-8 -t UTF-8 gbk.bin
(空 / 乱码)
# 按 GBK 去读(正确)
$ iconv -f GBK -t UTF-8 gbk.bin
中文
这就是乱码的全部原理:字节没坏,读的规则错了。
现代 UTF-8 解码器遇到非法字节序列会丢字符或替换为 �,所以"中文直接消失"也是一种乱码形态——不是没写进去。
4. 四种成因,逐个验
4.1 源文件编码与 IDE 读取编码不一致
| 现象 | 判断 | 修法 |
|---|---|---|
| 打开文件就是乱码(还没运行) | 文件本身是 GBK,IDE 按 UTF-8 打开 | 右下角编码 → Reload in GBK → 再 Convert to UTF-8(先重载再转换,顺序反了会二次损坏) |
| 部分文件乱、部分正常 | 混用编码 | 统一转 UTF-8;.editorconfig 加 charset = utf-8(S01-B04 §5.1) |
根因:项目里有人用 Windows 记事本(默认 GBK)写了文件,提交时没转码。
4.2 编译期编码
javac 读源文件用的编码由 -encoding 决定。Maven 里对应:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
症状:源文件里中文正常,编译后 System.out 出现乱码 / 字符串常量在字节码里已损坏。
验证:mvn -X compile 里搜 -encoding,确认是 UTF-8。缺省时取决于平台默认编码——这就是"Maven 在 Windows 上编译中文乱码"的来源。
4.3 运行时默认编码(JVM)
# JDK 8/11 + Windows:显式指定
java -Dfile.encoding=UTF-8 -jar app.jar
# 或在 IDEA 的 VM options / 运行配置里加
判断是不是这一层:只在"运行时输出"乱、源文件和编译都没问题时,就是它。
顺序问题:-Dfile.encoding 必须在启动参数里,程序里 System.setProperty("file.encoding", ...) 在 JDK 18+ 基本无效(字符集在启动时已确定)。
4.4 显示端(IDE 控制台 / 终端 / Windows 代码页)
| 环境 | 检查 | 修法 |
|---|---|---|
| IDEA 控制台 | Settings → Editor → General → Console 的 Default encoding |
设为 UTF-8 |
| IDEA 全局 | Settings → Editor → File Encodings:Global / Project / Properties 三处都 UTF-8 |
见 B04 |
| Windows 终端 | chcp(936 = GBK,65001 = UTF-8) |
chcp 65001(仅当前会话);持久化要改终端设置或系统区域 |
| Linux/macOS 终端 | locale 里的 LC_ALL/LANG |
设 LANG=zh_CN.UTF-8 或 C.UTF-8 |
关键区分:chcp 65001 只影响当前控制台窗口。新建窗口又变回 936——很多人"改完好了,重启又乱",就是这一条。
4.5 字体(旧文的第一条方案,其实很少是原因)
字体缺失的表现不是"乱码"而是方块/问号。区分方法:
- 乱码(乱字符)→ 编码问题
- 方块/豆腐块 → 字体问题(缺中文字形)
旧文把"设置支持中文的字体"列为方案一,优先级应该是最后——它是"显示层缺字形",不是"字节错"。
5. 排障决策树
乱码出现
│
① 还没运行,打开文件就乱?
│ 是 → 源文件编码(§4.1):Reload 正确编码 → Convert to UTF-8
│ 否 ↓
② 编译后乱、运行时乱?
│ 是 → 编译期(§4.2):project.build.sourceEncoding / -encoding
│ 否 ↓
③ 只在运行/输出时乱?
│ 是 → JVM 默认编码(§4.3):看 java -XshowSettings 的 stdout.encoding
│ 否 ↓
④ 只有控制台/终端里乱,文件本身正常?
│ 是 → 显示端(§4.4):IDE Console encoding / chcp / locale
│ 否 ↓
⑤ 显示成方块而不是乱字符?
是 → 字体(§4.5)
每一步都有独立命令可验,不要跳步试。
6. 一套"一次配好"的最小集合
| 层 | 配置 | 放哪 |
|---|---|---|
| 仓库 | .editorconfig:charset = utf-8 |
仓库根(团队共享) |
| Maven | project.build.sourceEncoding=UTF-8 |
pom.xml |
| JVM | 只在 JDK 8/11 + Windows 时加 -Dfile.encoding=UTF-8 |
运行配置 / Dockerfile |
| IDE | File Encodings 三处 UTF-8 + Console encoding UTF-8 | 个人设置(导出进 settings.zip,S01-B09) |
| 终端 | Windows Terminal 默认 UTF-8;Linux LANG=*.UTF-8 |
个人/机器 |
这五处配好,跨平台协作的乱码问题基本消失。 判据:同一份代码在 Windows 与 Linux 上编译运行,输出一致。
7. 旧文对照
| 旧文方案(A-1377) | 内容 | 本篇处理 |
|---|---|---|
| 一 | 设置支持中文的字体 | §4.5 降为最后一步,并区分"方块 vs 乱码" |
| 二 | File Encodings 三处设 UTF-8 | §4.1、§6 保留,补「Reload 再 Convert」的顺序 |
| 三 | 改 IDEA 配置文件加 -Dfile.encoding=UTF-8 |
§4.3 保留但标注版本条件(JDK 18+ 通常无需) |
| 四 | 控制台编码设为 UTF-8 | §4.4 保留 |
| 五 | Tomcat 控制台乱码 | 归入 §4.4(容器日志编码属显示端 + JVM 参数) |
| — | 无诊断顺序、无四层模型 | 新增 §2–§5:四层模型 + 决策树 + java -XshowSettings 实测 |
8. 自测
- "乱字符"与"方块"分别指向哪一层问题?
- JDK 18 之后为什么很多项目不再需要
-Dfile.encoding=UTF-8?那什么时候仍然需要? - Maven 项目里中文乱码,先查
pom.xml的哪个属性? chcp 65001之后为什么"重启又乱了"?- IDEA 里把 GBK 文件"Convert to UTF-8"之前,为什么必须先
Reload in GBK?
9. 下一篇
S01-J07 · 依赖治理:依赖树、冲突、BOM 与私服
编码问题解决后,下一个"看不见的坑"是依赖:同一个库三个版本、传递依赖偷偷降级、私服绕过后编译通过但运行炸。下一篇用 dependency:tree -Dverbose 与 dependency:analyze 把冲突逼到明面上,并给出 BOM 与私服的治理方案。
本篇属于系列《S01 · 开发环境与工具链》。旧文来源:A-1377;扩展点 E-S01-021/022。JVM 编码属性与 GBK 错配为 2026-11-05 实测。