JavaSE🔟IO 流
前言
1、流
流(Stream):内存与存储设备之间传输数据的通道。
分类:
- 按方向:以内存为基准。
- 输入流:读操作,存储设备 → 内存。
- 输出流:写操作,内存 → 存储设备。
- 按处理单位:
- 字节流:字节,可以读写任意类型数据。
- 字符流:字符,只能读写文本类型数据。
- 按功能:
- 节点流:实际具有传输数据的功能(具体构件)。
- 过滤流:在节点流的基础之上增强功能(装饰者)
2、字节流
2.1、抽象类
InputStream
字节输入流
- read():从输入流中读取数据的下一个字节并返回。
- read(byte[]):从输入流中读取数据的一部分字节,存储到缓冲区并返回读取字节数量。
- read(byte[], off, len):从输入流中的偏移量 off 开始读取 len 个字节,存储到缓冲区并返回读取字节数量。
- close():关闭输入流,释放资源。
OutputStream
字节输出流
- write(int):将指定字节(ASCII 码)写入输出流。
- write(byte[]):将指定字节数组写入输出流。
- write(byte[], off, len):将指定字节数组中偏移量 off 开始的 len 个字节,写入到输出流。
- flush():刷新输出流,并将任何缓冲的字节写出。
- close():先调用 flush(),再关闭输出流,释放资源。
Hint:
write()
仅将数据写入流中,此时位于内存,还未写到磁盘文件。flush()
才将数据写到磁盘文件。
2.2、节点流
FileInputStream
使用步骤
-
创建流:参数为文件名
-
创建缓冲区:减少磁盘 I/O,提高效率
-
读取数据:直接读取磁盘文件
-
关闭流
public void readFile(String fileName) throws IOException { // 1、创建流 FileInputStream fis = new FileInputStream(fileName); // 2、缓冲区 byte[] buf = new byte[1024]; int len = 0; // 3、读取 while ((len = fis.read(buf)) != -1) { System.out.println(new String(buf, 0, len)); } // 4、关闭流 fis.close(); }
FileOutputStream
使用步骤
-
创建流:参数 1 为文件名,参数 2 表示是否写入方式。
- true:追加内容到文件中。
- false:覆盖文件。
-
写数据
-
刷新到磁盘
-
关闭流
public void writeFile(String location) throws IOException { // 1、创建流 FileOutputStream fos = new FileOutputStream(location, true); // 2、写 String str = "Hello, I/O world!"; fos.write(str.getBytes()); // 3、刷新 fos.flush(); // 4、关闭流 fos.close(); }
case:文件复制
思路:输入流读取文件数据,输出流写数据。
-
创建流
-
创建缓冲区:减少磁盘 I/O,提高效率
-
读写数据
-
关闭流:先开后关
public void copyFile(String source, String dest) throws IOException { // 1、创建流 FileInputStream fis = new FileInputStream(source); FileOutputStream fos = new FileOutputStream(dest); // 2、缓冲区 byte[] buf = new byte[1024]; int len = 0; // 3、读写 while ((len = fis.read(buf)) != -1) { fos.write(buf, 0, len); } // 4、关闭流:先开后关 fos.close(); fis.close(); }
2.3、缓冲流
Buffered Stream
属于过滤流(装饰者)
- 优点:提高 I/O 效率,减少磁盘访问次数。
- 内置缓冲区:在节点流的基础上增强的功能。
Buffered Stream
内置了一个缓冲区(默认大小 8K)。- 调用者对
Buffered Stream
的操作,都是在内置缓冲区的操作。
- 使用:
- 创建
Buffered Stream
时,需要传入节点流。 - 关闭
Buffered Stream
时,自动关闭节点流。
- 创建
读写流程
- 读:缓冲输入流
Buffered Stream
先使用节点流,读取一部分字节数到内置缓冲区。- 在内置缓冲区的数据读完之前,都是从内置缓冲区中读取。
- 写:缓冲输出流
Buffered Stream
先使用节点流,写入一部分字节数到内置缓冲区。- 在内置缓冲区的数据写完之前,都是从内置缓冲区中写出。
BufferedInputStream
使用步骤
Hint:此处缓冲区是节点流用于批量读取字节的缓冲区,不是缓冲流的内置缓冲区。
public void readFile(String filename) throws IOException {
// 1、创建流
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
// 2、缓冲区(Hint)
byte[] buf = new byte[1024];
int len = 0;
// 3、读取
while ((len = bis.read(buf)) != -1) {
System.out.print(new String(buf, 0, len));
}
// 3、关闭流
bis.close();
}
BufferedOutputStream
使用步骤
public void writeFile(String location) throws IOException {
// 1、创建流
FileOutputStream fos = new FileOutputStream(location);
BufferedOutputStream bos = new BufferedOutputStream(fos);
// 2、写
String str = "Hello, buffer I/O";
for (int i = 0; i < 10; i++) {
bos.write(str.getBytes());
// 3、刷新
bos.flush();
}
// 4、关闭流
bos.close();
}
case:文件复制
思路:输入流读取文件数据,输出流写数据。
-
创建流:节点流、过滤流
-
创建缓冲区
-
读写数据
-
关闭流:只需关闭过滤流,内部自动关闭节点流。
public void copyFile(String source, String dest) throws IOException { // 1、 创建流 FileInputStream fis = new FileInputStream(source); FileOutputStream fos = new FileOutputStream(dest); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos); // 2、缓冲区 byte[] buf = new byte[1024]; int len = 0; // 3、读写 while ((len = bis.read(buf)) != -1) { bos.write(buf, 0, len); bos.flush(); } // 4、关闭连接 bos.close(); bis.close(); }
2.4、对象流
ObjectStream
字节流的子类,属于过滤流
增强功能:
- 内置缓冲区
- 读写对象:
- 序列化:向流中写入一个对象
- 反序列化:从流中读取一个对象
👉 对象序列化
序列化 - ObjectOutputStream
使用步骤
-
创建流
-
写数据:即序列化对象
-
关闭流
public void write(String location) throws IOException { // 1、创建流 FileOutputStream fos = new FileOutputStream(location); ObjectOutputStream oos = new ObjectOutputStream(fos); // 2、序列化对象 Person p1 = new Person("Jaywee", 17); oos.writeObject(p1); oos.flush(); // 3、关闭流 oos.close(); }
反序列化 - ObjectInputStream
使用步骤
-
创建流
-
读数据:即反序列化对象
-
关闭流
public Person read(String name) throws IOException, ClassNotFoundException { // 1、创建流 FileInputStream fis = new FileInputStream(name); ObjectInputStream ois = new ObjectInputStream(fis); // 2、反序列化对象 Person p = (Person) ois.readObject(); // 3、关闭 ois.close(); return p; }
3、字符流
当字符的编码格式和解码格式不一致时,会出现乱码。
- 计算机中有多种编码字符集,使用的字节数也不同。
- 若使用字节流,将字符拆分为多个字节进行读写,可能导致乱码。
使用字符流,将字符作为整体进行读写。
(操作与字节流相同,区别是数据单位)
3.1、抽象类
Reader
字符输入流
Writer
字符输出流
3.2、节点流
FileReader
public void readFile(String fileName) throws IOException {
// 1、创建流
FileReader fr = new FileReader(fileName);
// 2、逐个字符读取
int data = 0;
// fr.read()返回数据的下一个字符
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
// 3、关闭
fr.close();
}
FileWriter
public void writeFile(String location) throws IOException {
// 1、创建流
FileWriter fw = new FileWriter(location);
// 2、写
String str = "Hello, I/O world!";
fw.write(str);
// 3、刷新
fw.flush();
// 4、关闭流
fw.close();
}
case:文件复制
对比字节流
-
缓冲区类型:
- 字节流缓冲区:byte
- 字符流缓冲区:char
-
文件类型:
-
字节流:任意类型文件。
-
字符流:文本类型文件。
public void copyFile(String source, String dest) throws IOException { // 1、创建流 FileReader fr = new FileReader(source); FileWriter fw = new FileWriter(dest); // 2、缓冲区 char[] buf = new char[1024]; int len = 0; // 3、读取 while ((len = fr.read(buf)) != -1) { fw.write(buf, 0, len); } // 4、关闭流:先开后关 fw.close(); fr.close(); }
-
3.3、缓冲流
Buffered Stream
(属于过滤流,装饰者)
对比字节缓冲流:
- 支持输入换行符:
- 字节流:需手动输入换行符(Linux
\n
,Windows\r\n
) - 字符缓冲输出流:提供
newLine()
,返回操作系统对应的换行符。
- 字节流:需手动输入换行符(Linux
- 支持读一行:
readLine()
BufferedReader
示例:逐行读取
public void read(String name) throws IOException {
// 1、创建流
FileReader fr = new FileReader(name);
BufferedReader br = new BufferedReader(fr);
// 2、逐行读取
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// 3、关闭
br.close();
}
BufferedWriter
public void write(String location) throws IOException {
// 1、创建流
FileWriter fw = new FileWriter(location);
BufferedWriter bw = new BufferedWriter(fw);
// 2、写
String str = "Hello, buffer I/O";
for (int i = 0; i < 10; i++) {
bw.write(str);
// 换行符
bw.newLine();
bw.flush();
}
// 3、关闭
bw.close();
}
3.4、打印流
3.4.1、说明
PrintWriter
Writer 子类,属于过滤流
增强功能:
-
封装打印方法:
print()
,println()
。 -
数据原样打印:如不会将数字转换为字符。
// print()部分重载方法 public void print(boolean b) { write(b ? "true" : "false"); } public void print(char c) { write(c); } public void print(int i) { write(String.valueOf(i)); } // println(String) public void println(String x) { synchronized (lock) { print(x); println(); } }
3.4.2、使用
public void write(String location) throws IOException {
// 1、创建流
FileWriter fw = new FileWriter(location);
PrintWriter pw = new PrintWriter(fw);
// 2、打印
pw.print(97);
pw.print(7.2);
pw.print('c');
pw.println("Jaywee");
pw.print(true);
// 3、刷新
pw.flush();
// 4、关闭
pw.close();
}
4、转换流
适配器模式:字节流 → 字符流,
可显式设置默认字符编码(解码)方式。
- 被适配者:字节流(InputStream/InputStream)
- 适配者:字符流(Reader/Writer)
- 适配器类:转换流(InputStreamReader/InputStreamWriter)
InputStreamReader
-
将字节输入流转换为字符输入流
-
可显式设置解码方式(Decoder)
public void read(String name) throws IOException { // 1、创建流 FileInputStream fis = new FileInputStream(name); // 可指定解码方式:CharsetDecoder InputStreamReader reader = new InputStreamReader(fis); // 2、读 int data = 0; while ((data = reader.read()) != -1) { System.out.print((char) data); } // 3、关闭 reader.close(); }
OutputStreamWriter
-
将字节输出流转换为字符输出流
-
可显式设置解码方式(Encoder)
public void write(String location) throws IOException { // 1、创建流 FileOutputStream fos = new FileOutputStream(location); // 可指定解码方式:CharsetEncoder OutputStreamWriter writer = new OutputStreamWriter(fos); // 2、写 String str = "Hello, the bridge from byte streams to character stream"; writer.write(str); writer.flush(); // 3、关闭 writer.close(); }