Java IO(Input/Output)是 Java 中处理数据输入输出的核心 API,用于实现程序与外部设备(如文件、网络、内存等)的数据交互。
IO流核心概念
流(Stream)
- 定义:流是 Java 中数据传输的抽象表示,如同 “水流” 一样,数据以有序的、连续的方式在程序与外部设备(文件、网络、内存等)之间传输。
- 特性:
- 流是单向的:输入流只能读数据,输出流只能写数据。
- 流是顺序的:数据按顺序传输,不能随机访问(如不能直接跳到流的中间位置读写)。
- 流是可关闭的:流会占用系统资源(如文件句柄),使用后必须关闭以释放资源。
输入与输出(Input/Output)
- 输入(
Input):数据从外部设备(如文件、键盘、网络)进入程序的过程(读操作),由输入流(InputStream/Reader体系)实现。 - 输出(
Output):数据从程序发送到外部设备(如文件、屏幕、网络)的过程(写操作),由输出流(OutputStream/Writer体系)实现。 - 方向基准:以程序为参照物,数据 “进入程序” 为输入,“离开程序” 为输出。
数据源(Data Source)与数据目的地(Data Sink)
- 数据源:提供数据的源头,是输入流的读取对象,如:
- 物理设备:文件、硬盘、U 盘。
- 网络资源:Socket 连接、HTTP 响应。
- 内存资源:字节数组、字符数组。
- 数据目的地:接收数据的目标,是输出流的写入对象,与数据源类型对应(文件、网络、内存等)。
缓冲(Buffer)机制
- 定义:缓冲是处理流(如
BufferedInputStream、BufferedReader)内部维护的一块内存区域,用于临时存储数据。 - 作用:减少直接与外部设备的 IO 交互次数(如磁盘读写、网络传输),因为内存操作比外部设备操作快得多,从而大幅提升效率。
- 原理:
- 读操作:先将数据从外部设备读入缓冲区,程序从缓冲区取数据(缓冲区空了再读外部设备)。
- 写操作:程序先将数据写入缓冲区,缓冲区满了再一次性写入外部设备(可通过
flush()强制刷新)。
编码与解码(字符流核心)
- 编码(
Encode):字符 → 字节(如将'中'转换为 UTF-8 编码的字节[-28, -72, -83])。 - 解码(
Decode):字节 → 字符(如将 UTF-8 字节[-28, -72, -83]转换为'中')。 - 字符流的本质:字符流 = 字节流 + 编码表,通过
InputStreamReader/OutputStreamWriter可指定编码(如 UTF-8),避免文本处理时的乱码问题。
流的分类体系
整体概览:Java 流(Stream)的分类体系是理解 IO 操作的核心框架,主要通过三个维度进行划分,形成了层次清晰的分类体系
按数据流向分类
以程序为参照物,根据数据传输的方向划分:
| 类型 | 定义 | 核心基类 | 典型用途 |
|---|---|---|---|
| 输入流 | 数据从外部设备进入程序(读操作) | InputStream(字节)、Reader(字符) | 读取文件、接收网络数据等 |
| 输出流 | 数据从程序发送到外部设备(写操作) | OutputStream(字节)、Writer(字符) | 写入文件、发送网络数据等 |
特点:输入流只能读不能写,输出流只能写不能读,单向传输是流的核心特性。
按处理数据的单位分类
根据流操作的数据最小单位划分:- 字节流
- 单位:以字节(8 位二进制)为基本处理单位。
- 适用场景:所有类型数据(文本、图片、音频、视频等二进制数据)。
- 核心基类:
- 输入:
InputStream(抽象类,所有字节输入流的父类)。 - 输出:
OutputStream(抽象类,所有字节输出流的父类)。
- 输入:
- 典型实现类:
- 节点流:
FileInputStream、FileOutputStream(直接操作文件字节)。 - 处理流:
BufferedInputStream、BufferedOutputStream(缓冲增强)、DataInputStream、DataOutputStream(读写基本数据类型)。
- 节点流:
- 字符流
- 单位:以字符(16 位 Unicode 编码)为基本处理单位。
- 适用场景:仅文本数据(如 .txt、.java 文件),自动处理编码转换。
- 核心基类:
- 输入:
Reader(抽象类,所有字符输入流的父类)。 - 输出:
Writer(抽象类,所有字符输出流的父类)。
- 输入:
- 典型实现类:
- 节点流:
FileReader、FileWriter(直接操作文本,但依赖系统编码)。 - 处理流:
InputStreamReader、OutputStreamWriter(字节流转字符流,指定编码)、BufferedReader、BufferedWriter(缓冲 + 按行读写)。
- 节点流:
核心区别:字节流处理原始二进制数据,字符流处理经过编码的文本数据(解决乱码问题)。
按功能角色分类
根据流与数据源的关系及功能划分:- 节点流(低级流)
- 定义:直接连接数据源(如文件、内存、网络)的流,是 IO 操作的 “底层通道”。
- 特点:必须与具体数据源绑定,无法独立存在,是所有流操作的基础。
- 典型实现:
- 字节节点流:
FileInputStream、FileOutputStream(连接文件)、ByteArrayInputStream(连接内存字节数组)。 - 字符节点流:
FileReader、FileWriter(连接文本文件)。
- 字节节点流:
- 处理流(高级流 / 包装流)
- 定义:不直接连接数据源,而是包装节点流或其他处理流,用于增强功能或简化操作。
- 特点:依赖被包装的流(“装饰者模式”),可多层嵌套,提升效率或扩展功能。
- 典型实现及功能:
- 缓冲流(
BufferedXXX):增加缓冲区,减少 IO 次数,提高效率。 - 转换流(
InputStreamReader/OutputStreamWriter):字节流与字符流互转,指定编码。 - 对象流(
ObjectInputStream/ObjectOutputStream):实现对象的序列化与反序列化。 - 打印流(
PrintWriter):提供格式化输出、自动换行等便捷功能。
- 缓冲流(
分类体系总结表
| 分类维度 | 细分类型 | 核心基类 / 接口 | 核心特点 | 典型场景 |
|---|---|---|---|---|
| 数据流向 | 输入流 | InputStream、Reader | 读数据到程序,单向 | 读取文件、接收网络数据 |
| 输出流 | OutputStream、Writer | 从程序写数据,单向 | 写入文件、发送网络数据 | |
| 数据单位 | 字节流 | InputStream、OutputStream | 处理字节,适用于所有数据类型 | 图片、视频、音频等 |
| 字符流 | Reader、Writer | 处理字符,适用于文本,解决编码问题 | 文本文件读写 | |
| 功能角色 | 节点流 | FileInputStream等 | 直接连接数据源,底层基础流 | 与文件、内存直接交互 |
| 处理流 | BufferedReader等 | 包装其他流,增强功能(缓冲、转换等) | 提高效率、简化操作 |
常用类
字节流(处理二进制数据)
字节流以byte为单位处理数据,适用于所有类型文件(如图片、音频、视频、文本等)。核心基类是抽象类InputStream(输入)和OutputStream(输出)。
输入字节流(InputStream)
FileInputStream
从文件读取字节数据常用方法:read()(读单个字节)、read(byte[] b)(读字节到数组)、close()(关闭流)。
// 读取文件内容(字节流)
try (FileInputStream fis = new FileInputStream("test.txt")) {
byte[] buffer = new byte[1024]; // 缓冲区
int len;
// 循环读取,len为实际读取的字节数(-1表示结束)
while ((len = fis.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len)); // 转成字符串输出
}
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream
带缓冲区的字节输入流,通过减少 IO 次数提高效率(包装其他 InputStream)。
常用方法与InputStream一致,底层维护缓冲区。
// 缓冲流读取(效率更高)
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("test.txt"))) {
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayInputStream
从字节数组读取数据(内存中的输入流)。
适用于需要将字节数组当作 “数据源” 的场景。
输出字节流(OutputStream)
FileOutputStream
向文件写入字节数据。常用方法:write(int b)(写单个字节)、write(byte[] b)(写字节数组)、flush()(刷新缓冲区)、close()。
// 写入文件(字节流)
String content = "Hello, IO Stream!";
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
fos.write(content.getBytes()); // 字符串转字节数组写入
fos.flush(); // 强制刷新(确保数据写入磁盘)
} catch (IOException e) {
e.printStackTrace();
}
BufferedOutputStream
带缓冲区的字节输出流,减少 IO 次数(包装其他 OutputStream)。
// 缓冲流写入
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("output.txt"))) {
bos.write("Buffered Output".getBytes());
bos.flush(); // 缓冲流需手动刷新(或关闭时自动刷新)
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayOutputStream
向字节数组写入数据(内存中的输出流),可通过toByteArray()获取结果。
字符流(处理文本数据)
字符流以char为单位处理数据(适合文本文件,如.txt、.java),核心基类是抽象类Reader(输入)和Writer(输出)。
输入字符流(Reader)
FileReader
从文件读取字符数据(默认使用系统编码)。常用方法:read()(读单个字符)、read(char[] cbuf)(读字符到数组)、close()。
// 字符流读取文本
try (FileReader fr = new FileReader("text.txt")) {
char[] buffer = new char[1024];
int len;
while ((len = fr.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader
带缓冲区的字符输入流,提供`readLine()`方法(读取整行文本),效率更高。// 缓冲字符流读取(支持读行)
try (BufferedReader br = new BufferedReader(
new FileReader("text.txt"))) {
String line;
// 逐行读取(null表示结束)
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
StringReader
从字符串读取字符数据(内存中的字符输入流)。输出字符流(Writer)
FileWriter
向文件写入字符数据(默认系统编码)。常用方法:write(int c)(写单个字符)、write(char[] cbuf)、write(String str)(写字符串)、flush()、close()。
// 字符流写入文本
try (FileWriter fw = new FileWriter("output.txt")) {
fw.write("Hello, 字符流!"); // 直接写字符串
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}
BufferedWriter
带缓冲区的字符输出流,提供newLine()方法(跨平台换行)。
// 缓冲字符流写入(支持换行)
try (BufferedWriter bw = new BufferedWriter(
new FileWriter("output.txt"))) {
bw.write("第一行");
bw.newLine(); // 换行(自动适配系统)
bw.write("第二行");
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
StringWriter
向字符串缓冲区写入字符数据,可通过toString()获取结果。
转换流(字节流 ↔ 字符流)
字节流与字符流的桥梁,用于指定编码格式处理文本(解决中文乱码问题)。
InputStreamReader:将字节输入流转换为字符输入流(指定编码)。OutputStreamWriter:将字节输出流转换为字符输出流(指定编码)。
// 指定编码读取(解决乱码)
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream("text.txt"), "UTF-8"); // 指定UTF-8编码
BufferedReader br = new BufferedReader(isr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// 指定编码写入
try (OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("output.txt"), "UTF-8"); // 指定UTF-8编码
BufferedWriter bw = new BufferedWriter(osw)) {
bw.write("中文内容(UTF-8编码)");
} catch (IOException e) {
e.printStackTrace();
}
其他常用流
DataInputStream / DataOutputStream
读写基本数据类型(如int、double),保持数据类型不变。
// 写入基本类型
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream("data.bin"))) {
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeUTF("字符串"); // 写入UTF-8字符串
} catch (IOException e) {
e.printStackTrace();
}
// 读取基本类型
try (DataInputStream dis = new DataInputStream(
new FileInputStream("data.bin"))) {
int num = dis.readInt();
double d = dis.readDouble();
String str = dis.readUTF();
System.out.println(num + ", " + d + ", " + str);
} catch (IOException e) {
e.printStackTrace();
}
ObjectInputStream / ObjectOutputStream
用于对象的序列化(写入)和反序列化(读取),要求类实现Serializable接口。
// 序列化对象
class User implements Serializable {
String name;
int age;
// 构造方法、getter/setter...
}
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.obj"))) {
User user = new User("Alice", 20);
oos.writeObject(user); // 写入对象
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.obj"))) {
User user = (User) ois.readObject(); // 读取对象
System.out.println(user.name + ", " + user.age);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}