在 Java 开发中,你是否曾遇到过这样的场景?
- 从数据库读取中文显示为
??? - 文件读写后中文变成
我爱Java - HTTP 接口返回的 JSON 中文全是乱码
- 控制台输出中文却显示方块或问号
这些问题的根源,几乎都指向同一个“幕后黑手”——字符编码(Character Encoding)。
本文将带你深入 Java 中的字符编码机制,从底层原理到实战解决方案,彻底告别乱码困扰。
一、什么是字符编码?为什么需要它?
计算机只认识 0 和 1,但人类使用的是文字(如中文、英文、日文等)。字符编码就是建立“字符”与“二进制数字”之间映射关系的规则。
举个例子:
- 字符
'A'→ 编码为01000001(ASCII) - 字符
'中'→ 编码为11100100 10111000 10101101(UTF-8)
没有统一的编码规则,计算机就无法正确存储和传输文本。
二、常见字符编码简介
| 编码名称 | 特点 | 适用场景 |
|---|---|---|
| ASCII | 7位,128个字符(英文、数字、符号) | 英文系统基础 |
| ISO-8859-1 (Latin-1) | 8位,256字符,兼容ASCII | 西欧语言 |
| GBK / GB2312 | 中文编码,双字节 | 中国大陆旧系统 |
| UTF-8 | 可变长(1~4字节),兼容ASCII,支持全球字符 | 现代 Web、Java 默认推荐 |
| UTF-16 | 固定2或4字节,Java 内部使用 | Java 字符串底层表示 |
| UTF-32 | 固定4字节,简单但浪费空间 | 少数系统使用 |
✅ 关键结论:UTF-8 是当前互联网事实标准,应作为项目默认编码。
三、Java 中的字符编码机制
1. Java 内部统一使用 UTF-16
Java 的 char 类型是 16 位无符号整数,每个 char 存储一个 UTF-16 编码单元。
char c = '中';
System.out.println((int)c); // 输出 20013(Unicode 码点 U+4E2D)
对于超出 BMP(基本多文种平面)的字符(如 emoji 😊),Java 使用 代理对(Surrogate Pair) 表示:
String emoji = "😊";
System.out.println(emoji.length()); // 输出 2!因为用了两个 char
System.out.println(emoji.codePointCount(0, emoji.length())); // 正确:1 个字符
💡 建议:处理 Unicode 字符时,优先使用
codePointAt()、codePointCount()等方法。
2. 字节与字符串的桥梁:Charset
Java 通过 java.nio.charset.Charset 类管理编码转换:
import java.nio.charset.StandardCharsets;
String text = "你好,Java!";
// 字符串 → 字节数组(编码)
byte[] utf8Bytes = text.getBytes(StandardCharsets.UTF_8);
byte[] gbkBytes = text.getBytes(Charset.forName("GBK"));
// 字节数组 → 字符串(解码)
String fromUtf8 = new String(utf8Bytes, StandardCharsets.UTF_8);
String fromGbk = new String(gbkBytes, "GBK");
⚠️ 危险操作:不要使用无参
getBytes()或new String(byte[])!
它们会使用 系统默认编码(如 Windows 是 GBK,Linux 是 UTF-8),导致跨平台乱码。
四、常见乱码场景与解决方案
场景 1:控制台输出中文乱码
原因:控制台编码与 Java 输出编码不一致。
解决方案:
- IDEA / Eclipse:设置控制台编码为 UTF-8(File → Settings → Editor → File Encodings)
- 命令行:
- Windows:
chcp 65001切换到 UTF-8 模式 - Linux/macOS:确保
LANG=en_US.UTF-8
- Windows:
// 强制指定输出编码(不推荐,应统一环境)
PrintStream out = new PrintStream(System.out, true, "UTF-8");
out.println("你好");
场景 2:文件读写乱码
错误写法:
// 使用默认编码,跨平台风险极高!
Files.write(Paths.get("data.txt"), "中文内容".getBytes());
String content = new String(Files.readAllBytes(Paths.get("data.txt")));
正确写法:
// 显式指定 UTF-8
Path file = Paths.get("data.txt");
Files.write(file, "中文内容".getBytes(StandardCharsets.UTF_8));
String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
// 或使用 Files.readString (Java 11+)
Files.writeString(file, "中文内容", StandardCharsets.UTF_8);
String text = Files.readString(file, StandardCharsets.UTF_8);
场景 3:Web 接口返回乱码(Spring Boot)
问题:REST API 返回 JSON 中文显示为 ???。
解决方案:
- 确保 Controller 返回
String时指定编码:@GetMapping(value = "/text", produces = "text/plain;charset=UTF-8") public String getText() { return "你好,世界!"; } - 全局配置(
application.properties):server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true - 使用
@RestController+ 对象返回(自动 JSON 序列化,通常默认 UTF-8):@GetMapping("/user") public User getUser() { return new User("张三", 25); // Jackson 自动处理编码 }
场景 4:数据库中文乱码
排查步骤:
- 数据库表字符集是否为
utf8mb4(MySQL)?CREATE TABLE users ( name VARCHAR(100) ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - JDBC 连接 URL 是否指定编码?
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
五、Java 项目编码规范建议
-
统一使用 UTF-8:
- 源代码文件保存为 UTF-8(IDE 设置)
- 构建工具(Maven/Gradle)指定编码:
<!-- Maven --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>
-
永远显式指定 Charset:
// 好 new String(bytes, StandardCharsets.UTF_8); // 坏 new String(bytes); // 依赖系统默认 -
避免混合编码:整个系统(前端、后端、数据库、文件)应统一为 UTF-8。
六、高级技巧:检测文件编码
Java 本身不提供自动检测编码的功能,但可借助第三方库如 juniversalchardet:
import org.mozilla.universalchardet.UniversalDetector;
byte[] buf = Files.readAllBytes(Paths.get("unknown.txt"));
UniversalDetector detector = new UniversalDetector(null);
detector.handleData(buf, 0, buf.length);
detector.dataEnd();
String encoding = detector.getDetectedCharset(); // 如 "UTF-8", "GB18030"
🔍 注意:自动检测并非 100% 准确,仅作参考。
七、结语:编码虽小,影响全局
字符编码是软件国际化(i18n)的基石。在 Java 项目中,坚持“显式优于隐式”原则,始终明确指定 UTF-8,就能避开 99% 的乱码问题。
记住这句口诀:
“输入指定编码,输出指定编码,文件统一 UTF-8,数据库也要跟上!”
互动话题:
你在项目中踩过哪些字符编码的“坑”?是如何解决的?欢迎在评论区分享你的经验!
2万+

被折叠的 条评论
为什么被折叠?



