Java 程序在读取、处理或输出中文时,参与的几个环节(如文件、控制台、JVM 内部)使用了不同的“语言字典”(即字符编码),导致翻译错误,从而显示为乱码。

下面我将从 “为什么会乱码”、“如何排查” 到 “如何彻底解决”,为你提供一个全面且系统的解决方案。
核心问题根源:字符编码不一致
想象一个场景:
- 你有一个用
UTF-8编码保存的你好.txt文件。 - 你的 Linux 终端(Terminal)默认使用
GBK编码来显示文字。 - 你的 Java 程序使用
GBK编码去读取这个UTF-8文件。
这时,Java 用 GBK 的“字典”去读 UTF-8 的“书”,自然就读错了,显示为乱码。
乱码问题通常发生在以下几个环节的编码不匹配:

| 环节 | 描述 | 常见编码 |
|---|---|---|
| 源文件编码 | 你的 .java 源代码文件本身保存时使用的编码。 |
UTF-8 (推荐), GBK |
| JVM 编码 | Java 虚拟机在编译和运行时默认使用的编码。 | JDK 8 及以前 依赖系统默认,JDK 9+ 默认为 UTF-8 |
| 文件/网络编码 | 程序读取的文件(如 .txt, .properties, .json)或网络请求的编码。 |
UTF-8 (最常见), GBK, ISO-8859-1 |
| 终端/控制台编码 | 你在 Linux 终端看到程序输出结果时,终端所使用的显示编码。 | UTF-8 (现代 Linux), GBK (一些旧系统或中文环境) |
只要这四个环节中有任意两个不一致,就可能出现乱码。
如何排查和定位问题?
在解决问题之前,先要学会诊断,用下面的命令来确定你的环境编码。
检查终端/控制台编码
在 Linux 终端中输入:
echo $LANG echo $LC_CTYPE
输出结果会告诉你当前环境的语言和字符集。
en_US.UTF-8:表示环境是英文,使用 UTF-8 编码。zh_CN.GBK:表示环境是中文,使用 GBK 编码。C:这是一个非常经典的“陷阱”,表示使用 ASCII 编码,不支持中文,几乎所有涉及中文的操作都会乱码。
检查 JVM 默认编码
Java 程序启动时,JVM 会从系统环境继承编码,你可以通过一个简单的 Java 程序来查看它:
public class CheckEncoding {
public static void main(String[] args) {
System.out.println("file.encoding: " + System.getProperty("file.encoding"));
System.out.println("sun.jnu.encoding: " + System.getProperty("sun.jnu.encoding"));
}
}
编译并运行:
javac CheckEncoding.java java CheckEncoding
file.encoding:JVM 默认的文件编码,会影响文件读写。sun.jnu.encoding:JVM 默认的控制台编码,会影响System.out.println()的输出。
注意:file.encoding 在 JDK 9+ 中默认是 UTF-8,但在 JDK 8 及更早版本中,它通常是系统的默认编码(GBK)。
分类解决方案
根据乱码发生的场景,采取不同的解决方案。
Java 源代码文件(.java)中的中文注释/字符串乱码
问题:编译时或 IDE 中提示乱码。
原因:.java 文件的保存编码与编译器期望的编码不一致。
解决方案:
最佳实践:强制所有 Java 源文件使用 UTF-8 编码。
-
保存文件时:在使用 IDE(如 IntelliJ IDEA, Eclipse)时,设置默认编码为 UTF-8。
- IntelliJ IDEA:
File->Settings->Editor->File Encodings-> 将Global Encoding和Project Encoding都设置为UTF-8。 - Eclipse:
Window->Preferences->General->Workspace-> 将Text file encoding设置为UTF-8。
- IntelliJ IDEA:
-
编译时:如果使用命令行编译,可以显式指定编码:
# -encoding: 指定源文件使用的编码 javac -encoding UTF-8 YourFile.java
读取文本文件(如 .txt, .csv)乱码
问题:用 FileReader 或 BufferedReader 读取文件内容,中文显示为乱码。
原因:代码中未指定编码,使用了 file.encoding(可能是错误的);或者指定的编码与文件实际编码不一致。
错误示范:
// 错误:FileReader 使用的是平台默认编码,可能不是 UTF-8
try (FileReader fr = new FileReader("你好.txt")) {
// ...
}
正确示范:
永远不要使用 FileReader/FileWriter,它们使用的是平台默认编码,不可靠。
应该使用 InputStreamReader/OutputStreamWriter,并显式指定编码。
import java.io.*;
public class ReadFile {
public static void main(String[] args) {
// 假设文件是 UTF-8 编码
String filePath = "你好.txt";
try (InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "UTF-8");
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
控制台(Terminal)输出乱码
问题:System.out.println() 输出的中文显示为乱码。
原因:JVM 内部使用的编码(sun.jnu.encoding)与终端的显示编码不一致。
JVM 用 GBK 编码输出,但终端用 UTF-8 解读,就会乱码。
解决方案:
-
治本之策:修改 JVM 启动参数 在启动 Java 程序时,通过
-D参数显式指定 JVM 的编码,使其与终端编码一致。# 假设你的终端编码是 UTF-8 java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 YourApp # 假设你的终端编码是 GBK java -Dfile.encoding=GBK -Dsun.jnu.encoding=GBK YourApp
注意:
-Dfile.encoding也会影响文件读写,请确保它与你读取/写入的文件编码匹配。 -
治标之策:修改终端编码(不推荐,但有时有效) 你可以临时修改当前终端的编码,让它适应你的程序。
# 对于支持 locale 的终端,可以临时切换到 GBK 环境 export LANG=zh_CN.GBK export LC_ALL=zh_CN.GBK # 然后运行你的 Java 程序 java YourApp # 注意:这只会影响当前终端会话
这种方法治标不治本,因为你的程序在别的环境可能又会乱。
读取 .properties 配置文件乱码
问题:.properties 文件中的中文值读取为乱码(如 \u4f60\u597d)。
原因:.properties 文件在 Java 中默认使用 ISO-8859-1 编码读取,如果文件是用 UTF-8 保存的,直接读取就会乱码。
解决方案:
-
JDK 9 及以上版本: 在
java命令中添加参数--encoding=UTF-8,这是最简单的方法。java --encoding=UTF-8 -jar your-app.jar
-
JDK 8 及以下版本: 在读取
Properties对象后,对包含中文的值进行转码。import java.io.*; import java.util.Properties; public class ReadProperties { public static void main(String[] args) { Properties props = new Properties(); try (InputStreamReader isr = new InputStreamReader( new FileInputStream("config.properties"), "UTF-8")) { props.load(isr); // 直接获取可能还是乱码,因为Properties的store/load方法内部有处理 // 对于JDK 8,如果值是中文,需要额外处理 String value = props.getProperty("message"); if (value != null && value.startsWith("\\")) { // 如果是unicode转义序列,需要转换 value = unicodeToChinese(value); } System.out.println("读取到的值: " + value); } catch (IOException e) { e.printStackTrace(); } } // 将Unicode转义字符串转换为中文 public static String unicodeToChinese(String str) { StringBuilder sb = new StringBuilder(); int begin = 0; int index = str.indexOf("\\u"); while (index != -1) { sb.append(str.substring(begin, index)); sb.append((char) Integer.parseInt(str.substring(index + 2, index + 6), 16)); begin = index + 6; index = str.indexOf("\\u", begin); } sb.append(str.substring(begin)); return sb.toString(); } }
数据库读写乱码
问题:从 MySQL 数据库读取的中文是乱码,或者存入数据库后变成问号 。 原因:JDBC 连接字符串、数据库服务器、数据表/字段的字符集设置三者不匹配。
解决方案:
确保整个链路都使用 UTF-8。
-
数据库层面:
- 创建数据库时指定字符集:
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 创建表时指定字符集:
CREATE TABLE mytable (...) CHARACTER SET utf8mb4; - 确保 MySQL 服务器的配置文件 (
my.cnf) 中也设置了character-set-server = utf8mb4。
- 创建数据库时指定字符集:
-
JDBC 连接字符串层面: 在 JDBC URL 中显式指定编码。
// 在URL后面添加 ?useUnicode=true&characterEncoding=UTF-8 String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8"; Connection conn = DriverManager.getConnection(url, "user", "password");
终极解决方案与最佳实践
为了避免乱码问题,遵循以下黄金法则:
-
统一编码为 UTF-8:
- 所有 Java 源文件 (
.java) 保存为UTF-8(无 BOM)。 - 所有配置文件 (
.properties,.xml,.json,.txt) 保存为UTF-8。 - 数据库、表、字段 都使用
utf8mb4(比 utf8 能更好地支持特殊字符)。 - JVM 启动参数:
java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 ... - 终端环境:确保你的 Linux 终端使用
UTF-8编码 (echo $LANG查看)。
- 所有 Java 源文件 (
-
永远不要依赖默认编码:
- 在进行文件 I/O、网络 I/O 时,始终显式指定编码(如
StandardCharsets.UTF_8)。 - 使用
InputStreamReader/OutputStreamWriter代替FileReader/FileWriter。 - 使用
StandardCharsets.UTF_8代替字符串"UTF-8",可以避免拼写错误。
- 在进行文件 I/O、网络 I/O 时,始终显式指定编码(如
-
IDE 和构建工具统一配置:
- 在 Maven 或 Gradle 项目中,可以通过插件统一源文件编码。
- Maven:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> - Gradle:
tasks.withType(JavaCompile) { options.encoding = 'UTF-8' }
- Maven:
- 在 Maven 或 Gradle 项目中,可以通过插件统一源文件编码。
通过以上系统性的方法,你可以 99% 地解决 Linux 环境下的 Java 中文乱码问题,核心思想就是 “一切从源头控制,统一使用 UTF-8”。
