Java IO流体系的核心实质上是围绕“数据流动”构建的一套分层抽象。要理解它,无需死记硬背所有类名,关键在于把握三层逻辑:流向(输入/输出)、单位(字节/字符)、角色(节点/处理)。File类并不属于流体系,它仅负责管理路径和元数据;真正负责读写操作的是基于InputStream/OutputStream和Reader/Writer的两类流。

Java IO流体系本质上是围绕“数据流动”构建的分层抽象,核心不在于记住所有类名,而在于理解三层逻辑:流向(输入/输出)、单位(字节/字符)、角色(节点/处理)。File类不属于流体系,它只管理路径和元数据;真正负责读写的是基于InputStream/OutputStream和Reader/Writer的两类流。
先理清三组关键概念
流向决定方向:InputStream 和 Reader 是“往程序里拉数据”,OutputStream 和 Writer 是“把数据推出去”。别混淆——读文件用 FileInputStream 或 FileReader,写文件用 FileOutputStream 或 FileWriter。
单位决定适用场景:字节流(InputStream/OutputStream)处理一切二进制内容,图片、PDF、jar包都依赖它;字符流(Reader/Writer)专为文本设计,内部自动处理编码转换,但只适合纯文本(如 .txt、.csv、.json)。
角色决定是否直接操作文件:FileInputStream 是节点流,直连磁盘文件;BufferedInputStream 是处理流,必须套在 FileInputStream 外面才能用,它不自己打开文件,只给已有流增加缓冲能力。
File 类不是流,但它常是起点
File 类只做三件事:表示路径(new File("a.txt"))、查状态(exists()、length()、isDirectory())、做文件系统操作(mkdirs()、delete()、renameTo())。它不读不写内容,也不涉及编码。真正读写时,你需要把 File 对象传给 FileInputStream 或 FileReader 构造器。
常见误用:试图用 File 对象直接获取文件内容——不行。它只是“路牌”,不是“运输车”。
字节流是底层基石,优先掌握 FileInputStream / FileOutputStream
- 它们是最基础的文件读写实现,继承自 InputStream / OutputStream,支持所有类型文件
- 构造时可传 String 路径或 File 对象;若目标文件不存在,FileOutputStream 会自动创建;若存在,默认覆盖原内容(加 true 参数可追加)
- 务必用 read(byte[]) 而非 read() 单字节读取——后者性能极差,前者推荐 8192 字节(8KB)缓冲区
- 必须关闭流:用 try-with-resources 最安全,否则可能句柄泄漏
字符流用于文本,关键在编码控制
FileReader/FileWriter 看似简单,但默认使用系统编码(Windows 是 GBK),极易导致中文乱码。生产环境几乎不用它们直接构造。
正确做法是组合使用:
- FileInputStream + InputStreamReader → 指定 UTF-8 编码读文本
- FileOutputStream + OutputStreamWriter → 指定 UTF-8 编码写文本
- 再套一层 BufferedReader / BufferedWriter 提升效率,支持按行读写
例如读 UTF-8 文本:
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) { ... }
}
装饰器模式是扩展灵魂,别硬记类名,记链式逻辑
IO 流强大之处不在单个类,而在组合能力。比如要安全高效读一个带中文的文本文件,典型链条是:
- FileInputStream(节点流,打开文件)
- → InputStreamReader(转换流,指定 UTF-8 解码)
- → BufferedReader(缓冲流,提升读取速度,支持 readLine)
写入同理:FileOutputStream → OutputStreamWriter → BufferedWriter。每加一层,就增加一项能力(编码转换、缓冲、格式化等),且各层职责单一,可自由拆装。
不需要死记 BufferedInputStream 是谁的子类,只需知道:它必须包装一个 InputStream 才能工作,作用就是加缓冲。
