介 绍
Java 的 IO(输入 / 输出)流是处理设备间数据传输的核心机制,主要用于读取和写入数据。
IO 流根据不同的分类方式可以分为多种类型:
- 按流向划分:输入流(InputStream/Reader)和输出流(OutputStream/Writer)
- 按数据类型划分:字节流(InputStream/OutputStream)和字符流(Reader/Writer)
- 按功能划分:节点流(直接操作数据源)和处理流(对节点流进行包装)
核心用法、区别及适用场景
一、字节流(InputStream/OutputStream)
特点:以字节为单位处理数据,可操作所有类型文件(文本、图片、音频等)。
1. 节点字节流
- FileInputStream/FileOutputStream(文件字节流)
适用场景:读写二进制文件(图片、视频)或简单文本文件。// 读取文件 try (FileInputStream fis = new FileInputStream("input.txt")) { byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { // 处理读取的字节数据 } } // 写入文件 try (FileOutputStream fos = new FileOutputStream("output.txt")) { byte[] data = "Hello".getBytes(); fos.write(data); // 写入字节数组 }
2. 处理字节流
BufferedInputStream/BufferedOutputStream(缓冲字节流)
核心优势:内部维护缓冲区,减少磁盘 IO 次数,大幅提升大文件处理效率。// 带缓冲区的文件读写(性能更优) try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"))) { byte[] buffer = new byte[8192]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); // 批量写入,减少IO次数 } }
DataInputStream/DataOutputStream(数据字节流)
适用场景:需要按数据类型读写时(如保存配置文件、网络传输基本类型)。// 读写基本数据类型(保留数据类型信息) try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"))) { dos.writeInt(100); // 写入int dos.writeDouble(3.14); // 写入double dos.writeUTF("字符串"); // 写入UTF字符串 } try (DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"))) { int num = dis.readInt(); double d = dis.readDouble(); }
ObjectInputStream/ObjectOutputStream(对象流)
// 序列化对象(需实现Serializable接口) class User implements Serializable { private static final long serialVersionUID = 1L; // 版本控制 private String name; // get/set... } // 序列化 try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.obj"))) { oos.writeObject(new User("张三")); } // 反序列化 try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.obj"))) { User user = (User) ois.readObject(); }
核心特点:实现对象的持久化存储或网络传输,
transient
修饰的字段不参与序列化。
二、字符流(Reader/Writer)
特点:以字符为单位处理数据,自动处理编码转换,仅适用于文本文件。
1. 节点字符流
- FileReader/FileWriter(文件字符流)
局限性:依赖系统默认编码,可能导致跨平台乱码。// 读取文本(默认编码) try (FileReader fr = new FileReader("text.txt")) { char[] buffer = new char[1024]; int len; while ((len = fr.read(buffer)) != -1) { System.out.print(new String(buffer, 0, len)); } } // 写入文本 try (FileWriter fw = new FileWriter("output.txt")) { fw.write("中文文本"); // 直接写入字符 }
2. 处理字符流
InputStreamReader/OutputStreamWriter(转换流)
核心作用:字节流与字符流的桥梁,可指定编码(如 UTF-8),彻底解决乱码问题。// 指定编码读写(解决乱码问题) try (InputStreamReader isr = new InputStreamReader( new FileInputStream("text.txt"), StandardCharsets.UTF_8); OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) { char[] buffer = new char[1024]; int len; while ((len = isr.read(buffer)) != -1) { osw.write(buffer, 0, len); } }
BufferedReader/BufferedWriter(缓冲字符流)
核心优势:提供// 高效读写文本,支持按行操作 try (BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream("text.txt"), "UTF-8")); BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(new FileOutputStream("out.txt"), "UTF-8"))) { String line; while ((line = br.readLine()) != null) { // 按行读取 bw.write(line); bw.newLine(); // 跨平台换行 } }
readLine()
和newLine()
方法,适合处理换行符敏感的文本(如 CSV、配置文件)。PrintWriter(打印流)
适用场景:日志输出、格式化文本写入(如报表生成)。// 方便的文本输出(支持自动刷新) try (PrintWriter pw = new PrintWriter( new OutputStreamWriter(new FileOutputStream("log.txt"), "UTF-8"), true)) { pw.println("日志信息1"); // 自动换行 pw.printf("用户%s登录成功", "张三"); // 格式化输出 }
三、特殊流
ByteArrayInputStream/ByteArrayOutputStream(字节数组流)
适用场景:临时数据处理(如数据加密、压缩前的内存缓存)。// 内存中处理数据(无磁盘IO) byte[] data = "内存数据".getBytes(); try (ByteArrayInputStream bais = new ByteArrayInputStream(data); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int len; while ((len = bais.read(buffer)) != -1) { baos.write(buffer, 0, len); // 数据在内存中流转 } System.out.println(baos.toString()); }
PipedInputStream/PipedOutputStream(管道流)
适用场景:同一进程内的线程间数据传递。// 线程间通信 PipedInputStream pis = new PipedInputStream(); PipedOutputStream pos = new PipedOutputStream(pis); // 绑定输入输出流 // 线程1:写入数据 new Thread(() -> { try { pos.write("线程间通信".getBytes()); } catch (IOException e) { e.printStackTrace(); } }).start(); // 线程2:读取数据 new Thread(() -> { try { byte[] buf = new byte[1024]; pis.read(buf); } catch (IOException e) { e.printStackTrace(); } }).start();
四、核心区别与选择指南
维度 | 字节流 | 字符流 |
---|---|---|
处理单位 | 字节(8 位) | 字符(根据编码,如 UTF-8 为 1-4 字节) |
处理对象 | 所有文件(文本 + 二进制) | 仅文本文件 |
编码依赖 | 不依赖编码 | 依赖编码(需注意乱码) |
核心类 | InputStream/OutputStream | Reader/Writer |
选择原则:
- 二进制文件(图片、视频)→ 字节流 + 缓冲流
- 文本文件 → 字符流(优先用转换流指定编码)+ 缓冲流
- 基本数据类型 → Data 流
- 对象持久化 → 对象流
- 内存数据处理 → 字节数组流
- 线程通信 → 管道流
五、最佳实践
- 资源自动关闭:始终使用
try-with-resources
语法(JDK7+),无需手动调用close()
。 - 缓冲流优先:处理大文件时,用缓冲流包装节点流提升性能。
- 编码显式化:文本处理时通过
InputStreamReader/OutputStreamWriter
指定编码(如 UTF-8)。 - 序列化版本控制:实现
Serializable
的类必须定义serialVersionUID
,避免版本冲突。