Java IO 流-详解
介 绍
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(缓冲字节流)
// 带缓冲区的文件读写(性能更优) 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); // 写入intdos.writeDouble(3.14); // 写入doubledos.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(转换流)
// 指定编码读写(解决乱码问题) 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
,避免版本冲突。