字节流读写文本必须显式指定编码,否则必然乱码;应使用InputStreamReader/OutputStreamWriter包装并传入字符集(如UTF-8),避免依赖默认编码或手动new String(byte[])、getBytes(),且需注意缓冲刷新与资源关闭顺序。

字节流本身不处理编码,直接用它读写文本文件,乱码几乎是必然的。关键不是“能不能做”,而是“怎么做才不出错”——核心在于把编码转换这一步显式、可控地补上。
必须用 InputStreamReader / OutputStreamWriter 包装字节流
FileInputStream 和 FileOutputStream 只管字节,不能直接当文本用。要读文本,得用 InputStreamReader 把字节流“翻译”成字符流;要写文本,得用 OutputStreamWriter 把字符转回字节。
- 读文件:new InputStreamReader(new FileInputStream(file), "UTF-8")
- 写文件:new OutputStreamWriter(new FileOutputStream(file), "UTF-8")
- 千万别省略字符集参数——不写就用平台默认编码(Windows 是 GBK,Linux 是 UTF-8),跨环境必乱码
避免手动 new String(byte[]) 或 getBytes() 操作
这是最常见也最隐蔽的坑。比如从 FileInputStream 读出 byte[] 后直接调用 new String(bytes),没指定编码,就会走 JVM 默认 Charset,结果不可控。
- new String(bytes) → 等价于 new String(bytes, Charset.defaultCharset()),风险极高
- 正确做法:new String(bytes, "UTF-8") 或 new String(bytes, StandardCharsets.UTF_8)
- 同理,字符串转字节时,也必须明确写:str.getBytes(StandardCharsets.UTF_8)
复制或中转文本时,别混用字节流和字符流
比如用 FileInputStream 读,再用 FileWriter 写——这两者底层编码可能不一致,中间没有统一解码/编码环节,极易出错。
- 安全组合只有两种:字节流 ↔ 字节流(纯搬运,不管内容),或字符流 ↔ 字符流(全程带编码)
- 如果非得跨层(如网络接收字节、业务处理字符),必须在边界处显式转换,并固定编码
- 示例:HTTP 响应体是字节,但你知道它是 UTF-8 文本,那就立刻用 InputStreamReader(响应流, UTF_8) 包一层再读
注意缓冲与关闭顺序,防止截断或丢数据
OutputStreamWriter 内部有缓冲,不 flush 或 close,最后一段内容可能卡在内存里没写出。
- 写完务必调用 flush() 或 close()
- 推荐用 try-with-resources,自动保证 close,且 close 会隐式 flush
- 不要在 finally 里单独关底层字节流(如 FileOutputStream),那会破坏字符流的关闭逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











