字节流和字符流的核心区别有三点:一是处理单位不同,字节流以byte(8位)处理所有二进制数据,字符流以char(16位Unicode)专用于文本并自动编解码;二是编码依赖性,字符流必须显式指定编码(如UTF-8),字节流不涉及编码;三是基类与场景,字节流基类为InputStream/OutputStream,适用于图片、网络传输等通用IO,字符流基类为Reader/Writer,适用于.txt、XML等文本处理。

Java IO流面试题重在厘清分类逻辑、设计意图和典型误点,不是背类名,而是讲清“为什么这么分”“什么时候用哪个”。下面按高频考点直给回答要点,不绕弯。
字节流和字符流的区别是什么?
核心就三点:
-
处理单位不同:字节流以
byte(8位)为单位,能读写任意二进制数据(图片、音频、jar包);字符流以char(16位Unicode)为单位,专用于文本,内部会做字节↔字符的编码转换。 - 是否依赖编码:字符流必须指定编码(如UTF-8),否则用系统默认编码,极易乱码;字节流不涉及编码,原样搬运字节。
-
基类与适用场景:字节流基类是
InputStream/OutputStream,适合文件复制、网络传输等通用场景;字符流基类是Reader/Writer,适合处理.txt、.csv、XML等纯文本内容。
节点流和处理流有什么区别?
这是装饰器模式的落地体现:
-
节点流(也叫低级流):直接连数据源,比如
FileInputStream读磁盘文件、SocketInputStream读网络数据。它不能少,是IO操作的起点。 -
处理流(也叫高级流):不直接连数据源,而是包装一个已有的流(节点流或其他处理流),用来增强功能。比如
BufferedInputStream加缓冲、InputStreamReader做字节→字符转换、ObjectInputStream支持反序列化。 -
关键提醒:处理流必须有被包装的对象,单独创建会报
NullPointerException或IllegalArgumentException。
为什么要用缓冲流(比如BufferedReader)?
本质是用空间换时间,解决频繁小IO的性能瓶颈:
立即学习“Java免费学习笔记(深入)”;
- 普通
FileReader每次read()都触发一次系统调用,读一个字符就访问一次磁盘,效率极低; -
BufferedReader内部维护一个字符数组(默认8192字符),首次读取时批量从底层流拉一大块数据进内存,后续readLine()直接从内存拿,减少IO次数; -
注意:写操作中
BufferedWriter的flush()不能省——未满缓冲区的数据不会自动落盘,关流会自动flush,但异常提前退出时可能丢数据。
如何正确读写文本文件并避免乱码?
别用FileReader/FileWriter——它们用系统默认编码,跨环境必翻车。标准做法是:
- 读文本:
new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"), "UTF-8")) - 写文本:
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"), "UTF-8")) - 更现代写法(Java 7+):
Files.newBufferedReader(Paths.get("a.txt"), StandardCharsets.UTF_8)
统一显式指定UTF-8,从源头消灭乱码。
对象序列化怎么实现?要注意什么?
用ObjectOutputStream和ObjectInputStream,但前提是类实现Serializable接口:
- 序列化:
oos.writeObject(obj),把对象状态转成字节流写入文件或网络; - 反序列化:
ois.readObject(),从字节流重建对象(要求类定义没变,且serialVersionUID一致); -
敏感字段要加
transient修饰,否则密码、临时计算结果也会被存进去; - 序列化不是深拷贝的替代方案——它只保存对象图的可序列化部分,静态字段、方法、构造器都不参与。


















