Charset.forName() 不抛 UnsupportedEncodingException,而是抛 IllegalArgumentException;真正抛该异常的是 String.getBytes(String) 等老式 API;JDK 7+ 内置常见编码,但需传标准 IANA 名称(如 "UTF-8"),避免别名或拼写错误。

Charset.forName() 为什么抛出 UnsupportedEncodingException?
这个异常其实不会在 Charset.forName() 中抛出——它声明抛出的是 IllegalArgumentException。真正抛出 UnsupportedEncodingException 的是老式 API,比如 String.getBytes(String) 或 new String(byte[], String)。这是初学者最容易混淆的点。
根本原因在于:JDK 7+ 内置了所有常见编码(UTF-8、ISO-8859-1、GBK、US-ASCII 等),只要传入标准名称(如 "UTF-8",注意连字符不能写成下划线或空格),Charset.forName() 就不会失败;但若传入 "utf8"(缺连字符)、"GB2312 "(尾部空格)、"utf-8"(小写连字符实际可接受,但大小写混用如 "Utf-8" 在某些旧 Android 版本可能挂)就可能触发异常或返回 null。
- 始终用
Charset.availableCharsets().keySet()查看当前 JVM 实际支持的名称列表 - 优先使用
StandardCharsets.UTF_8这类静态常量,完全避免字符串拼写风险 - 如果必须用字符串,只从 IANA 注册名中选(如
"UTF-8"、"ISO-8859-1"、"GBK"),不依赖平台别名
Charset.encode() 和 String.getBytes(Charset) 的行为差异
Charset.encode() 接收 CharBuffer,返回 ByteBuffer,走的是 NIO 编码通道;而 String.getBytes(Charset) 是传统 IO 路径,内部也调用 Charset.encode(),但做了额外封装(比如自动处理 BOM、处理替换策略)。两者在默认配置下结果一致,但关键区别在错误处理上。
-
Charset.encode()默认使用CodingErrorAction.REPORT,遇到无法编码的字符(如用 ISO-8859-1 编码中文)直接抛CharacterCodingException -
String.getBytes(Charset)则默认静默替换为?(取决于 Charset 的newEncoder()配置) - 想让
Charset.encode()也替换而非报错,需手动设置:charset.newEncoder().onUnmappableCharacter(CodingErrorAction.REPLACE)
GBK 和 GB2312 编码在 Java 里能互换使用吗?
不能直接互换,尽管 GBK 是 GB2312 的超集。Java 中 "GBK" 和 "GB2312" 对应不同 Charset 实例,它们的 newDecoder() 行为不同:用 GB2312 解码一个纯 GBK 扩展字(如“镕”、“啰”)会抛 MalformedInputException;反过来,用 GBK 解码 GB2312 文本完全没问题。
立即学习“Java免费学习笔记(深入)”;
- 生产环境一律用
"GBK",不要用"GB2312"—— 后者在 OpenJDK 17+ 已被标记为 deprecated - Windows 记事本保存为“ANSI”时,实际用的是系统默认编码(中文 Windows 即 GBK),不是 GB2312
- 若需兼容老旧系统限制,必须显式校验字节流是否只含 GB2312 范围(0xA1–0xFE 的双字节,且首字节 ≤ 0xF7),不能靠 Charset 名称猜测
如何安全地检测字节流的真实编码?
Java 标准库不提供编码检测功能。Charset.isSupported() 只判断名称是否合法,不分析字节内容。真实场景中,靠 Charset.decode() 尝试解码并捕获异常,既不可靠(如 UTF-8 和 GBK 对部分乱码都可能“成功”解出可读文本),又慢。
- 轻量级方案:用
juniversalchardet(Mozilla charset detector 的 Java 移植),对 2–4KB 字节块识别率约 85% - 更稳方案:强制约定协议层携带编码信息(HTTP Content-Type、XML 声明、自定义 header),而非猜
- 最保险做法:服务端统一用 UTF-8 存储和传输,客户端明确声明
Content-Type: text/plain; charset=utf-8,绕过检测需求
编码检测本质是概率游戏,任何库都无法 100% 正确——尤其当输入短于 1KB 或含大量 ASCII 符号时,GBK、UTF-8、ISO-8859-1 可能给出相同“可信度”。别把它当成可交付功能来设计。










