该用字节流处理二进制数据(如图片、zip)、字符流处理文本(如.txt、.json),并显式指定utf-8编码;inputstreamreader/outputstreamwriter是桥接器,按需转码;混用时避免重复解码、漏设编码、误用缓冲。

字节流和字符流到底该用哪个
读写文件时选错流类型,轻则乱码,重则数据损坏。核心判断标准就一条:InputStream/OutputStream 处理原始二进制数据,Reader/Writer 处理带编码的文本。别看 FileInputStream 也能读中文,那是靠系统默认编码硬解,换台机器就可能崩。
- 处理图片、音频、ZIP 包、网络协议包——必须用字节流
- 读写 .txt、.json、.xml 等文本内容——优先用字符流,显式指定
UTF-8 - 从 socket 读 HTTP 响应体?先用字节流收完整响应,再按
Content-Type里的charset用InputStreamReader转成字符流
InputStreamReader 和 OutputStreamWriter 是怎么转码的
它们不是“转换器”,而是“桥接器”:把字节流套上编码规则,变成字符流接口。关键点在于,转码动作发生在每次 read() 或 write() 时,不是一次性全转完。
-
InputStreamReader内部有缓冲区,会按需从底层InputStream拉字节,再按指定 charset 解码成 char;遇到非法字节序列(比如 UTF-8 中断的 3 字节序列),默认抛MalformedInputException - 构造时没传 charset,它用
Charset.defaultCharset()——这玩意在 Windows 和 Linux 上大概率不同,线上出问题很难复现 - 别在循环里反复 new
InputStreamReader,每次新建都重置解码状态,会丢掉跨 buffer 的多字节字符(比如一个汉字被切在两个 read() 之间)
BufferedInputStream 和 BufferedReader 的缓冲区差异
两者都缓存数据提升性能,但缓存单位不同:前者缓字节,后者缓字符,导致行为差异很大。
-
BufferedInputStream的mark()/reset()可靠,因为字节位置确定;BufferedReader的mark()标记的是字符位置,但底层字节偏移不确定,reset 后可能读错字 -
BufferedReader.readLine()会吃掉换行符(\n、\r\n或\r),而BufferedInputStream.read()会原样返回这些字节 - 如果用
BufferedReader包裹InputStreamReader,又想保留原始换行符,不如直接用BufferedInputStream+ 自己按字节解析,更可控
字节流和字符流混用时最常踩的坑
最典型的错误是“套两层转换”:比如用 InputStreamReader 包一层 FileInputStream,再塞进 BufferedReader,最后还手动调 new String(bytes, "UTF-8") —— 这等于让同一段字节被解码三次。
立即学习“Java免费学习笔记(深入)”;
- 日志里出现 “” 或 “???”,八成是某处用了默认编码解码,另一处用了 UTF-8,编码链断了
- 用
PrintWriter写文件却没关自动 flush,或者忘了调flush(),内容卡在缓冲区没落盘 - 把
System.in直接当Reader用(比如new InputStreamReader(System.in)),不设缓冲,每敲一个字符都触发一次系统调用,交互卡顿
字符流背后永远拖着一个字节流,编码选择不是可选项,是契约。漏掉 charset 参数、跨流重复解码、混淆缓冲层级——这些问题不会报错,但会在某个用户上传含 emoji 的 CSV 时突然爆发。










