十一、java IO流

4 阅读5分钟

Java IO 体系


IO 核心认知

  • IO = Input/Output,用于数据传输(文件、网络、内存、控制台)
  • Java IO 核心是流(Stream):数据像水流一样有序传输
  • 流是单向的:要么读、要么写
  • 所有 IO 类都在 java.io. 包下

1. IO 流三大分类方式(必须背)

1.1 按数据流向

  • 输入流(Input):读数据 → 外部 → 程序
  • 输出流(Output):写数据 → 程序 → 外部

1.2 按操作数据单位

  • 字节流:以 byte 为单位,万能流,支持所有文件(图片/视频/文本)
  • 字符流:以 char 为单位,只支持纯文本,自带编码

1.3 按功能

  • 节点流(低级流):直接连接数据源(文件、数组、管道)
  • 处理流(包装流):包装节点流,增强功能(缓冲、对象、序列化)

2. IO 四大抽象基类(所有流的爹)

所有流都继承这 4 个抽象类:

  • 字节输入流InputStream
  • 字节输出流OutputStream
  • 字符输入流Reader
  • 字符输出流Writer

3. 字节流体系(万能流)

3.1 FileInputStream / FileOutputStream(文件节点流)

  • 作用:直接读写文件
  • 适用:所有文件类型
  • 特点:无缓冲区,读写效率低
FileInputStream fis = new FileInputStream("a.jpg");
FileOutputStream fos = new FileOutputStream("b.jpg");

3.2 BufferedInputStream / BufferedOutputStream(缓冲字节流)

  • 包装流,内置 8192 字节缓冲区
  • 减少磁盘 IO,速度极快
  • 企业开发首选
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"));

3.3 ByteArrayInputStream / ByteArrayOutputStream(内存数组流)

  • 操作内存中的 byte[]
  • 不涉及磁盘,速度最快
  • 常用于缓存、数据中转

3.4 ObjectInputStream / ObjectOutputStream(对象流)

  • 对象序列化 / 反序列化
  • 把对象写入文件/网络
  • 对象必须实现 Serializable 接口
  • transient 修饰的属性不参与序列化

3.5 DataInputStream / DataOutputStream(数据流)

  • 按 Java 基本数据类型读写:int、double、boolean、UTF 字符串
  • 常用于自定义协议、网络传输、二进制存储

3.6 PrintStream

  • 打印流,System.out 就是它
  • 不会抛 IO 异常
  • 可打印各种数据

4. 字符流体系(纯文本专用)

4.1 FileReader / FileWriter(文件字符流)

  • 直接读写文本文件
  • 缺陷:使用系统默认编码,跨平台必乱码

4.2 BufferedReader / BufferedWriter(缓冲字符流)

  • 高效、支持按行读写
  • 特有方法:
    • readLine() 读一行
    • newLine() 写换行
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

4.3 InputStreamReader / OutputStreamWriter(转换流

  • 字节流 ↔ 字符流 的桥梁
  • 可以指定编码(UTF-8/GBK)
  • 解决中文乱码唯一方案
  • 企业文本 IO 必须用它
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"), "UTF-8");

4.4 CharArrayReader / CharArrayWriter

字符数组流,操作内存 char 数组

4.5 PrintWriter

字符打印流,功能同 PrintStream


5. IO 流完整体系图谱(必背)

InputStream(字节输入)
 ├── FileInputStream
 ├── ByteArrayInputStream
 ├── FilterInputStream
 │    ├── BufferedInputStream
 │    ├── DataInputStream
 │    └── ObjectInputStream
 └── PipedInputStream(线程管道)

OutputStream(字节输出)
 ├── FileOutputStream
 ├── ByteArrayOutputStream
 ├── FilterOutputStream
 │    ├── BufferedOutputStream
 │    ├── DataOutputStream
 │    ├── ObjectOutputStream
 │    └── PrintStream
 └── PipedOutputStream

Reader(字符输入)
 ├── InputStreamReader(转换流)
 │    └── FileReader
 ├── BufferedReader
 ├── CharArrayReader
 └── StringReader

Writer(字符输出)
 ├── OutputStreamWriter(转换流)
 │    └── FileWriter
 ├── BufferedWriter
 ├── CharArrayWriter
 ├── StringWriter
 └── PrintWriter

6. 核心方法(必须掌握)

6.1 字节流方法

// 读
int read();                // 读一个字节,-1结束
int read(byte[] buf);      // 读一个数组
void close();

// 写
void write(int b);
void write(byte[] buf);
void write(byte[] buf,int off,int len);
void flush();
void close();

6.2 字符流方法

int read();
int read(char[] cbuf);
String readLine();

void write(String str);
void write(char[] cbuf);
void newLine();
void flush();

7. 各类流使用场景(企业开发标准)

7.1 复制任意文件(图片/视频/文件)

BufferedInputStream + BufferedOutputStream

7.2 读写文本文件、解决乱码

BufferedReader + InputStreamReader(指定UTF-8)

7.3 网络通信接收字符串

Socket 得到字节流 → 转换流 → 缓冲字符流

7.4 对象持久化/网络传输对象

ObjectInputStream / ObjectOutputStream

7.5 二进制协议、基本类型数据传输

DataInputStream / DataOutputStream

7.6 内存快速读写

ByteArrayInputStream / ByteArrayOutputStream


8. 企业开发 IO 巨坑(高频 BUG 合集)

坑1:流忘记关闭 → 文件句柄泄漏 → 服务器崩溃

  • 表现:Too many open files
  • 解决方案:JDK7+ try-with-resources

坑2:字符流不指定编码 → 中文乱码

  • FileReader/FileWriter 用系统编码
  • 必须用 InputStreamReader 指定 UTF-8

坑3:flush 忘记调用 → 数据写不出去

  • 缓冲区满才自动刷
  • 手动调用 flush 或 close 才会落地

坑4:close() 会自动 flush,但不要依赖

  • 业务逻辑异常直接跳走,数据丢失

坑5:循环逐字节读写,性能爆炸

  • 必须用数组缓冲:byte[8192] / char[8192]

坑6:序列化版本号不一致 → 反序列化失败

  • 实现 Serializable 建议显式声明:
private static final long serialVersionUID = 1L;

坑7:transient 变量被序列化(误解)

  • transient 修饰的属性不会序列化,默认值为 null / 0

坑8:装饰流关闭会自动关闭底层流

  • 只需要关闭最外层流

坑9:文本文件用字节流直接转字符串乱码

  • 必须用转换流指定编码

坑10:RandomAccessFile 并发不安全

  • 多线程操作同一文件会错乱

9. try-with-resources 自动关闭(企业标准)

实现 AutoCloseable 接口的流都能自动关闭:

try (
    FileInputStream fis = new FileInputStream("a.txt");
    InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
    BufferedReader br = new BufferedReader(isr)
) {
    String line = br.readLine();
} catch (IOException e) {
    e.printStackTrace();
}

无需 finally,自动关闭,不会泄漏


10. 传统 BIO 与 NIO 区别(扩展)

10.1 BIO(传统 IO)

  • 面向流
  • 阻塞 IO
  • 单向
  • 无缓冲区
  • 简单低效
  • 适合低并发

10.2 NIO(New IO / Non-blocking IO)

  • 面向缓冲区 Buffer
  • 非阻塞
  • 双向通道 Channel
  • 多路复用 Selector
  • 高并发、高性能
  • 适合网络通信(Netty 基于 NIO)

11. 高频 IO 面试题(100% 考)

  1. 字节流与字符流区别?
  2. 什么是节点流、处理流?
  3. 转换流作用与使用场景?
  4. 缓冲流为什么快?
  5. 序列化是什么?需要注意什么?
  6. try-with-resources 原理?
  7. flush 和 close 区别?
  8. 为什么字符流会乱码,字节流不会?
  9. 数据流作用?
  10. BIO 与 NIO 区别?