一、IO基础
1、什么是IO
IO,即 Input/Output,翻译过来就是输入输出 。在 Java 编程中,它是程序与外部世界进行数据交互的关键方式。简单来说,输入就是程序从外部获取数据,比如从文件中读取内容、接收网络传来的数据;输出则是程序将数据发送到外部,像将数据写入文件、向网络另一端发送数据。
打个比方,我们日常使用的手机,当我们从相册中打开一张照片时,照片的数据从手机存储设备进入到手机应用程序,这就类似于 Java 程序中的输入操作;而当我们拍摄一张新照片并保存到相册时,照片的数据从手机相机应用程序保存到手机存储设备,这就类似于 Java 程序中的输出操作。在 Java 世界里,IO 操作让程序能够与文件系统、网络、控制台等外部环境进行数据的交换,从而实现各种各样的功能。
2、JavaIO本质是在解决什么问题
Java IO 本质就干三件事:
读文件
写文件
网络数据传输(HTTP / Socket)
二、IO核心概念
1、流的概念
在 Java IO 中,流(Stream)是一个非常重要的概念。可以把流想象成一条数据传输的通道,数据就像水流一样在这个通道中流动。它具有方向性,数据从数据源流向程序(输入流),或者从程序流向数据目的地(输出流)。并且,流中的数据是按顺序依次传输的,就像水流是依次流淌一样 。数据在流中是以字节(byte)或者字符(char)为单位进行传输的。例如,当从一个文本文件中读取数据时,数据会以字节或者字符的形式通过输入流进入程序;当向文件写入数据时,数据会以字节或字符的形式通过输出流从程序流向文件。
2、输入流(InputStream)
用于从外部数据源读取数据到程序中。在 Java 中,所有输入流的基类是InputStream(字节输入流)和Reader(字符输入流) 。例如,当我们想要读取一个文本文件的内容时,可以使用FileInputStream(InputStream的子类)或者FileReader(Reader的子类)。
就是:把数据读进来
比如:读文件、读接口返回的数据、读图片、读前端上传的文件
3、输出流(OutputStream)
用于将程序中的数据写入到外部数据目的地。Java 中所有输出流的基类是OutputStream(字节输出流)和Writer(字符输出流) 。比如,要将一些文本内容写入文件,可以使用FileOutputStream(OutputStream的子类)或者FileWriter(Writer的子类)。以下是使用FileWriter写入文件的示例代码:
就是:把数据写出去
比如:写文件、把文件返回给前端、写日志文件、把图片保存到服务器
4、字节流
以字节(8 位)为单位处理数据,适用于处理任何类型的数据,包括二进制数据,如图片、音频、视频等 。InputStream和OutputStream是字节流的抽象基类,它们的子类如FileInputStream、FileOutputStream、ByteArrayInputStream、ByteArrayOutputStream等,广泛应用于各种字节数据的读写操作。例如,读取一张图片文件并输出其字节数据:
java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ByteStreamExample {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("image.jpg")) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
for (int i = 0; i < length; i++) {
System.out.print(buffer[i] + " ");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、字符流
以字符(16 位 Unicode)为单位处理数据,主要用于处理文本数据 。Reader和Writer是字符流的抽象基类,其常见子类有FileReader、FileWriter、BufferedReader、BufferedWriter等。字符流在处理文本时,会自动进行字符编码转换,方便处理不同编码格式的文本文件。比如,读取一个 UTF-8 编码的文本文件并输出内容:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class CharacterStreamExample {
public static void main(String[] args) {
try (Reader reader = new FileReader("text.txt");
BufferedReader bufferedReader = new BufferedReader(reader)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节流和字符流的主要区别在于处理数据的单位和应用场景。字节流更通用,可以处理任何类型的数据,但在处理文本时需要手动处理字符编码问题;字符流专门用于处理文本数据,自动处理字符编码,使用起来更方便,但不适用于二进制数据的处理。
6、 按功能分类
节点流:也称为低级流,是直接与数据源或数据目的地相连的流,负责从数据源读取数据或直接将数据写入数据目的地 。例如,FileInputStream和FileOutputStream直接操作文件,ByteArrayInputStream和ByteArrayOutputStream直接操作字节数组,它们都是节点流。以FileInputStream读取文件为例:
java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class NodeStreamExample {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("test.txt")) {
int data;
while ((data = inputStream.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
处理流:也叫包装流,是在节点流的基础上进行包装,增强了流的功能,如提高读写效率、增加数据处理能力等 。处理流不能单独存在,必须连接在其他流(通常是节点流)之上。例如,BufferedInputStream和BufferedOutputStream通过缓冲机制提高了数据读写的效率,它们通常会包装FileInputStream和FileOutputStream。下面是使用BufferedInputStream包装FileInputStream来提高读取效率的示例:
java
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ProcessingStreamExample {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream)) {
byte[] buffer = new byte[1024];
int length;
while ((length = bufferedInputStream.read(buffer)) != -1) {
System.out.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个例子中,BufferedInputStream在FileInputStream的基础上增加了缓冲区,减少了系统 I/O 操作的次数,从而提高了读取效率。处理流的存在使得 Java IO 的操作更加灵活和高效,开发者可以根据具体需求选择合适的处理流来包装节点流,实现各种复杂的数据处理功能。
三、Java IO 中常用的类
1、最基础的四个父类
这四个你要先认识,它们是所有 IO 类的"根"。
1. InputStream
作用: 按字节读取数据
适合: 图片、视频、pdf、压缩包、任意二进制数据
例子:
java
InputStream in = new FileInputStream("a.jpg");
int b = in.read();
in.close();
理解:read() 每次读取一个字节。
2. OutputStream
作用: 按字节写出数据
例子:
java
OutputStream out = new FileOutputStream("b.jpg");
out.write(97);
out.close();
这里 97 对应字节值,写出去后可能是字符 a。
3. Reader
作用: 按字符读取数据
适合: txt、json、xml、配置文件
例子:
java
Reader reader = new FileReader("a.txt");
int ch = reader.read();
reader.close();
4. Writer
作用: 按字符写出数据
例子:
java
Writer writer = new FileWriter("a.txt");
writer.write("你好");
writer.close();
2、文件流:和文件直接打交道
这是最常见的一组。
1. FileInputStream
作用: 从文件中读取字节
**例子:**读取图片
java
FileInputStream in = new FileInputStream("d:/test/a.jpg");
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
System.out.println("本次读取字节数:" + len);
}
in.close();
**什么时候用:**读图片、读 pdf、读压缩包、读任意文件原始内容
2. FileOutputStream
作用: 向文件中写字节
**例子:**写文件
java
FileOutputStream out = new FileOutputStream("d:/test/b.txt");
out.write("hello".getBytes());
out.close();
**什么时候用:**保存文件、下载文件到本地、写二进制内容
3. FileReader
作用: 从文件中读取字符
**例子:**读取文本
java
FileReader reader = new FileReader("d:/test/a.txt");
char[] buf = new char[1024];
int len;
while ((len = reader.read(buf)) != -1) {
System.out.print(new String(buf, 0, len));
}
reader.close();
什么时候用: 读普通文本文件、读日志文件、读配置文件
注意: FileReader 默认用系统编码,实际开发里更推荐 InputStreamReader 指定编码。
4. FileWriter
作用: 向文件中写字符
**例子:**写文本
java
FileWriter writer = new FileWriter("d:/test/a.txt");
writer.write("第一行\n");
writer.write("第二行\n");
writer.close();
**什么时候用:**写文本文件、生成日志、输出字符串内容
3、缓冲流:提高性能
缓冲流的思想很简单:别一次只读一个字节、写一个字节,先放到内存缓冲区里,再统一处理。
1. BufferedInputStream
作用: 给字节输入流加缓冲
**例子:**读取图片
java
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("d:/test/a.jpg")
);
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1) {
System.out.println("读取:" + len);
}
bis.close();
什么时候用:文件读操作几乎都可以加、网络输入也常加
2. BufferedOutputStream
作用: 给字节输出流加缓冲
**例子:**复制文件
java
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("d:/test/a.jpg")
);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("d:/test/b.jpg")
);
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
bos.close();
bis.close();
什么时候用:文件复制、文件下载、大文件写出
3. BufferedReader
作用: 给字符输入流加缓冲、可以按行读取
**例子:**按行读文本
java
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("d:/test/a.jpg")
);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("d:/test/b.jpg")
);
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
bos.close();
bis.close();
什么时候用: 读 txt、读日志、读配置文件、按行处理文本
这是文本处理里非常常用的类。
4. BufferedWriter
作用: 高效写文本
**例子:**写多行文本
java
BufferedWriter bw = new BufferedWriter(
new FileWriter("d:/test/a.txt")
);
bw.write("第一行");
bw.newLine();
bw.write("第二行");
bw.newLine();
bw.close();
**什么时候用:**写日志、写报表文本、写配置文件、输出多行字符串
4、转换流:处理编码问题
这两个类非常重要。
因为很多时候,底层拿到的是字节,但你要操作的是字符。
1. InputStreamReader
作用: 把字节输入流转换成字符输入流
**例子:**按 UTF-8 读取文本
java
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("d:/test/a.txt"),
"UTF-8"
)
);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
什么时候用: 读取中文文本、处理乱码、读取网络响应文本、读取接口返回的 JSON
理解:
FileInputStream负责从文件取字节。
InputStreamReader负责把字节按 UTF-8 变成字符。
BufferedReader负责按行读。
2. OutputStreamWriter
作用: 把字符写成指定编码的字节
**例子:**按 UTF-8 写文本
java
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("d:/test/a.txt"),
"UTF-8"
)
);
bw.write("你好,Java IO");
bw.newLine();
bw.write("第二行");
bw.close();
**什么时候用:**写中文文本、导出 txt/csv、往网络里写文本内容
5、内存流:数据不进文件,直接放内存里
这个很重要,很多接口开发里会遇到。
1. ByteArrayInputStream
作用: 从内存里的 byte[] 读取数据
例子:
java
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("d:/test/data.bin")
);
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeBoolean(true);
dos.close();
**什么时候用:**测试代码、模拟输入流、已经有 byte[],但下游方法要求传 InputStream
2. ByteArrayOutputStream
作用: 往内存里写字节,最后拿到完整 byte[]
例子:
java
DataInputStream dis = new DataInputStream(
new FileInputStream("d:/test/data.bin")
);
int a = dis.readInt();
double b = dis.readDouble();
boolean c = dis.readBoolean();
System.out.println(a);
System.out.println(b);
System.out.println(c);
dis.close();
**什么时候用:**拼接数据、临时缓存、文件下载前先组装内容、接口返回前先把内容写进内存,这是非常常用的内存流。
3. StringReader
作用: 把字符串当成输入流来读
例子:
java
StringReader sr = new StringReader("abc123");
int ch;
while ((ch = sr.read()) != -1) {
System.out.print((char) ch);
}
sr.close();
**什么时候用:**已经有字符串,但希望按 Reader 的方式处理它
4. StringWriter
作用: 把字符写到内存中的字符串里
例子:
java
StringWriter sw = new StringWriter();
sw.write("hello");
sw.write(" java");
String result = sw.toString();
System.out.println(result);
sw.close();
什么时候用: 在内存中拼接字符串、某些模板输出、替代 StringBuilder 的流式写法
6、数据流:按基本类型读写
1. DataOutputStream
作用: 按 Java 基本类型写数据
例子:
java
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("d:/test/data.bin")
);
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeBoolean(true);
dos.close();
2. DataInputStream
作用: 按写入顺序读取基本类型
例子:
java
DataInputStream dis = new DataInputStream(
new FileInputStream("d:/test/data.bin")
);
int a = dis.readInt();
double b = dis.readDouble();
boolean c = dis.readBoolean();
System.out.println(a);
System.out.println(b);
System.out.println(c);
dis.close();
什么时候用: 需要严格按类型保存和读取数据、Java 内部二进制格式处理
**注意:**读取顺序必须和写入顺序一致。
7、对象流:直接读写 Java 对象
1. ObjectOutputStream
作用: 把对象写出去
例子:
java
import java.io.Serializable;
class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("d:/test/user.obj")
);
User user = new User("张三", 20);
oos.writeObject(user);
oos.close();
2. ObjectInputStream
作用: 把对象读回来
例子:
java
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("d:/test/user.obj")
);
User user = (User) ois.readObject();
ois.close();
什么时候用: Java 对象持久化、Java 对象网络传输、学习序列化机制
注意: 类必须实现 Serializable。
| 类 | 作用 | 常见例子 |
|---|---|---|
| FileInputStream | 读文件字节 | 读图片、pdf |
| FileOutputStream | 写文件字节 | 保存文件 |
| FileReader | 读文件字符 | 读 txt |
| FileWriter | 写文件字符 | 写 txt |
| BufferedInputStream | 字节缓冲读 | 提高文件读取性能 |
| BufferedOutputStream | 字节缓冲写 | 高效复制文件 |
| BufferedReader | 字符缓冲读 | 按行读文本 |
| BufferedWriter | 字符缓冲写 | 高效写文本 |
| InputStreamReader | 字节转字符 | 解决乱码 |
| OutputStreamWriter | 字符转字节 | 指定编码输出 |
| ByteArrayInputStream | 从 byte[] 读 | 模拟输入 |
| ByteArrayOutputStream | 写到 byte[] | 内存拼接数据 |
| StringReader | 从字符串读 | 把字符串当流处理 |
| StringWriter | 写到字符串 | 内存中拼文本 |
| DataInputStream | 读基本类型 | readInt/readDouble |
| DataOutputStream | 写基本类型 | writeInt/writeDouble |
| ObjectInputStream | 读对象 | 反序列化 |
| ObjectOutputStream | 写对象 | 序列化 |
| PrintStream | 方便输出 | System.out |
| PrintWriter | 方便写文本 | println 输出文本 |
四、