关键在于源头指定正确编码,而非转编码;iso-8859-1不支持中文而utf-8支持,乱码主因是解码时字符集用错,必须在inputstreamreader构造时显式指定standardcharsets.iso_8859_1。

关键不是“转编码”,而是让 Java 正确理解原始字节的含义。ISO-8859-1 是单字节编码,不支持中文;UTF-8 是变长编码,能完整表示中文。乱码几乎都源于解码阶段用错了字符集——只要 InputStreamReader 一开始就没指定 ISO-8859-1,后面怎么“补救”都白搭。
源头指定编码,一步到位
读取 ISO-8859-1 编码的输入流时,必须在 InputStreamReader 构造时显式传入 StandardCharsets.ISO_8859_1:
- ✅ 正确写法:
new InputStreamReader(inputStream, StandardCharsets.ISO_8859_1) - ❌ 错误写法:
new InputStreamReader(inputStream)(依赖系统默认编码,Windows 常是 GBK,Linux/macOS 常是 UTF-8,必然错) - 此时
readLine()返回的 String 就是语义正确的 Unicode 字符串(Java 内部为 UTF-16),后续可安全使用getBytes(StandardCharsets.UTF_8)输出 UTF-8 字节
别用字符串“二次编码”来修复乱码
以下模式是典型误区,会导致不可逆失真:
- 先用默认编码错误解码,得到一个已损坏的 String
- 再调用
line.getBytes(ISO_8859_1)→ 实际是对乱码字符串“硬套” ISO-8859-1 字节,语义早已丢失 - 最后
new String(..., UTF_8)→ 把伪造字节当 UTF-8 解,结果仍是乱码 - 例如原始字节
0xE9(ISO 中是 é),若被 GBK 错解为某个汉字,再强行回写 0xE9,就彻底脱离原意
遇到未知编码时不能硬编码 ISO-8859-1
如果输入来源不可控(如用户上传文件、HTTP 响应体),盲目指定 ISO-8859-1 同样危险:
- 优先检查协议头:HTTP 的
Content-Type是否带charset=... - 检测 BOM(字节顺序标记):UTF-8 BOM 是
EF BB BF,UTF-16 是FF FE等 - 考虑用 ICU4J 或 juniversalchardet 做启发式检测(注意:ISO-8859-1 检测准确率低,慎用)
- 实在无法判断,至少加日志记录原始前几字节和猜测编码,便于事后排查
数据库或旧系统场景下的特殊处理
有些老系统把 UTF-8 字节误存为 ISO-8859-1 字符串(即“双编码”问题),表现为类似 䏿–‡ 的乱码文本:
- 这类字符串本质是:UTF-8 字节 → 被当成 ISO-8859-1 解码 → 得到错误字符 → 再存为字符串
- 修复方法:先按 ISO-8859-1 编码回字节,再用 UTF-8 解码:
new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8) - ⚠️ 注意:这仅适用于已知存在“双编码”的脏数据,不是通用转换逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











