Charset.forName() 抛出 IllegalArgumentException 而非 UnsupportedEncodingException;应使用 Charset.isSupported() 预检,注意规范名如 "UTF-8"(非 "utf8"),JDK 17+ 默认禁用非标准别名。

Charset.forName() 为什么抛出 UnsupportedEncodingException?
Java 的 Charset.forName() 实际上不会抛出 UnsupportedEncodingException —— 这是常见误解。该异常属于旧式 API(如 String.getBytes(String)),而 Charset.forName() 抛出的是 IllegalArgumentException,当传入非法或 JVM 不支持的字符集名时触发。
- 正确做法:用
Charset.isSupported("UTF-8")预检,返回false表示不支持(例如某些精简 JRE 缺少GBK) - 别写
Charset.forName("utf8"):规范名是"UTF-8"(带短横),"utf8"在部分 JDK 版本中可能失败 - JDK 17+ 默认禁用非标准别名(如
"GB2312"可能被拒绝),需显式启用系统属性-Dsun.nio.cs.map=legacy
new String(byte[], Charset) 和 String.getBytes(Charset) 性能差异在哪?
这两个操作看似对称,但底层行为不同:new String(byte[], Charset) 必须校验字节序列合法性(如 UTF-8 中是否含非法代理对或过长编码),而 String.getBytes(Charset) 是纯编码转换,无校验开销。
- 高频解码场景(如 HTTP body 解析)建议缓存
Charset实例:StandardCharsets.UTF_8比Charset.forName("UTF-8")快且无锁 - 若确定字节来源可信(如自己刚 encode 出来的),可用
new String(byte[], 0, length, charset)避免数组拷贝 - 注意:
String.getBytes(StandardCharsets.UTF_8)在 JDK 9+ 被内联优化,比传字符串名快 3–5 倍
CharsetEncoder/CharsetDecoder 如何避免隐式替换损坏数据?
默认的 CharsetEncoder 在遇到无法映射的字符时会插入 ?(MalformedInputAction.REPLACE),这容易掩盖编码问题。生产环境应主动控制策略。
- 解码时检测非法字节:用
decoder.onMalformedInput(CodingErrorAction.REPORT),抛出CharacterCodingException - 编码时处理不可表示字符:用
encoder.onUnmappableCharacter(CodingErrorAction.REPORT),而非默认的REPLACE - 不要依赖
String.replace("\ufffd", "...")修复 ——\ufffd是解码失败后的替换符,原始信息已丢失
Charset utf8 = StandardCharsets.UTF_8;
CharsetDecoder decoder = utf8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
try {
String s = decoder.decode(ByteBuffer.wrap(b)).toString();
} catch (CharacterCodingException e) {
// 此处可记录原始字节、上下文,用于定位源头编码错误
}
Windows 平台读取文件时 Charset 名称选哪个?
Windows 记事本保存为“ANSI”时实际使用的是系统区域设置对应的编码(如中文 Windows 是 GBK),不是 ISO-8859-1 或 US-ASCII。硬写 Charset.forName("GBK") 有风险:
立即学习“Java免费学习笔记(深入)”;
- Linux/macOS JVM 默认不支持
GBK(需额外加载 charsets.jar),JDK 17+ 更倾向用java.nio.charset.spi.CharsetProvider扩展 - 更健壮的做法:用
Files.readString(path, detectCharset(path))+ BOM 检测逻辑,或依赖Apache Tika/juniversalchardet做自动识别 - 如果必须指定,优先用
Charset.defaultCharset()—— 它返回当前 OS 的默认编码,但注意该值在启动后固定,不受运行时系统设置变更影响
new String(bytes, "GBK") 再 .getBytes("UTF-8"),如果原始 bytes 本身含 GBK 乱码,结果只会把乱码“转正”,而不是修复。真正需要的往往不是转换,而是从源头约束编码(如 HTTP Content-Type 头、数据库连接参数、IDE 文件编码设置)。










