解决中文乱码的关键是切断默认编码依赖、锁定真实编码、守住字节流源头;必须显式指定charset,确保inputstream未被提前读取,并优先依据http响应头或bom确定编码。

用 InputStreamReader 解决中文乱码,核心不是“转换技巧”,而是**切断默认编码依赖、锁定真实编码、守住字节流源头**。乱码不是程序出错,是解码规则和原始字节不匹配——只要三步做对,基本不会报错。
必须显式指定 Charset,别信“默认”
无参构造 new InputStreamReader(is) 是乱码头号元凶。它会调用 Charset.defaultCharset(),而这个值在 Windows 是 GBK,在 Linux/macOS 是 UTF-8,完全不可控。
- ✅ 正确写法:用
StandardCharsets.UTF_8(推荐,类型安全,不抛异常)或"UTF-8" - ✅ 文件是旧版 Windows 记事本“ANSI”保存?大概率是
StandardCharsets.GBK或"GBK" - ❌ 别猜、别省略、别写成
new InputStreamReader(is, Charset.defaultCharset())——这等于没指定
确保原始字节流未被提前读取
InputStreamReader 是装饰器,它从当前流指针位置开始解码。如果之前调过 is.read()、is.mark() 或其他读操作,字节就丢了开头,轻则乱码,重则 MalformedInputException 报错。
- 传入前检查:确认
inputStream是刚创建、未调用任何读方法的“新鲜”流 - 网络流特别注意:用
HttpURLConnection时,别在获取getInputStream()前调getResponseCode()以外的方法,某些实现会预读响应头甚至部分正文
搭配 BufferedReader 使用,兼顾效率与稳定性
InputStreamReader 只负责解码,不带缓冲、不支持按行读。单独用它逐字符 read() 不仅慢,还容易因异常中断导致资源泄漏。
- ✅ 推荐组合:
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) - ✅ 关闭只需
br.close(),它会自动级联关闭内部的 InputStreamReader 和底层 InputStream - ✅
br.readLine()返回的字符串,中文已完整解码,可直接打印、解析、拼接
Web 场景:以 HTTP 响应头为准,不硬写 UTF-8
网页返回的 Content-Type 常含 charset,比如 text/html; charset=gb2312 或 application/json; charset=utf-8。忽略它而统一用 UTF-8,遇到 GBK 编码的旧站照样报错。
- 先取头:用
conn.getContentType()解析出 charset 字段 - 再构造:把提取出的 charset 名(如 "gb2312")传给 InputStreamReader;若为空或无法识别,再 fallback 到 UTF-8
- 进阶提示:响应体开头若有 BOM(如
EF BB BF),也是 UTF-8 的强信号,可辅助判断











