【JAVASE | 第十五篇】IO流进阶:缓冲字节流和缓冲字符流
前言
在前面的学习中,我们已经接触了 Java 中最基础的 IO 流,比如 FileInputStream、FileOutputStream、FileReader、FileWriter。这些流可以完成最基本的文件读写操作,但是当文件变大、读写操作变多之后,就会发现普通流虽然能用,但是效率并不高。
所以这篇文章,我们就来学习 IO 流中非常重要的一部分内容:缓冲流。
我们主要学习下面几种缓冲流:
BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
这篇文章将重点总结一下缓冲字节输入输出流、缓冲字符输入输出流的特点,以及它们在实际开发中的常见使用方式。
引入
如果把普通流比作我们一趟一趟自己去搬东西,那么缓冲流就像给我们配了一辆小推车。
以前一次只能搬一点,现在一次可以搬更多。
来回跑的次数少了,效率自然也就提高了。
所以缓冲流最核心的思想就是:
在原有流的基础上增加缓冲区,减少和磁盘频繁交互的次数,从而提高读写效率。
一、为什么要学缓冲流
前面学过的普通流可以完成文件读写,但是它们往往存在两个问题:
- 读写效率不够高
- 处理文本时不够方便
而缓冲流正好可以解决这两个问题。
缓冲流可以分成两类:
- 缓冲字节流:适合处理所有类型文件,尤其适合复制图片、视频、音频等二进制文件
- 缓冲字符流:适合处理文本文件,读写更方便
二、缓冲字节流
1.BufferedInputStream 和 BufferedOutputStream 的基本使用
在项目中,缓冲字节流的使用方式如下:
InputStream src = new FileInputStream(srcpath);
InputStream bic = new BufferedInputStream(src);
OutputStream dest = new FileOutputStream(sicpath);
OutputStream bit = new BufferedOutputStream(dest);
byte[] bytes = new byte[1024];
int len;
while ((len = bic.read(bytes)) != -1) {
bit.write(bytes, 0, len);
}
从这段代码可以看出:
FileInputStream和FileOutputStream负责真正和文件建立连接BufferedInputStream和BufferedOutputStream负责在外面包一层缓冲,提高效率
也就是说:
缓冲字节流本质上是包装流,它必须依赖基础字节流使用。
2、缓冲字节流的特点
2-1. 自带缓冲区,读写效率更高
缓冲字节流内部自带缓冲池,不会每次都直接和磁盘发生交互,而是会先把一部分数据读到内存中,再从内存中取数据使用。
这样做的好处有:
- 减少磁盘访问次数
- 提高文件复制速度
- 适合频繁读写场景
这也是缓冲字节流和普通字节流最核心的区别。
2-2. 适合处理所有类型文件
因为字节流本身就适合处理任意类型文件,所以缓冲字节流同样适合:
- 图片
- 视频
- 音频
- 压缩包
- 可执行文件
- 任意二进制文件
2-3. 配合字节数组使用效果最好
虽然缓冲字节流本身已经提升了效率,但是如果仍然坚持“一个字节一个字节”地读写,性能依然不理想。
四种复制方式:
- 普通字节流,一个字节一个字节复制
- 普通字节流,按字节数组复制
- 缓冲字节流,一个字节一个字节复制
- 缓冲字节流,按字节数组复制
最推荐的方式是:
InputStream fis = new FileInputStream(SRC_FILE);
InputStream bis = new BufferedInputStream(fis);
OutputStream fos = new FileOutputStream(DEST_FILE + "4.avi");
OutputStream bos = new BufferedOutputStream(fos);
byte[] bytes = new byte[1024 * 32];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
结论:
缓冲流 + 数组读写,才是处理大文件时更推荐的组合。
三、缓冲字符流
缓冲字符流和缓冲字节流的核心思想一样,都是通过缓冲区提升效率。
不同点在于,字符流是专门面向文本内容的,所以它们更适合处理纯文本文件。
1.BufferedReader 的特点
1. 基本使用
Reader reader = new FileReader("day03-File-io\\src\\zifuxiaole2");
BufferedReader br = new BufferedReader(reader);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
2. 核心特点
(1)本质上也是包装流
和缓冲字节流一样,BufferedReader 也不是独立存在的,它是建立在 Reader 基础上的增强流。
常见写法就是:
Reader reader = new FileReader(...);
BufferedReader br = new BufferedReader(reader);
所以可以总结一句:
高级流一般都是对低级流做增强。
(2)读取文本效率更高
BufferedReader 内部同样带有缓冲区,处理字符数据时比普通 FileReader 更高效。
尤其适合读取:
.txt.java.xml.properties.md
这类纯文本文件。
(3)支持按行读取
这是 BufferedReader 最实用的特点之一:
br.readLine()
普通 FileReader 更多是按字符读取,而 BufferedReader 可以直接按行读取文本。
所以在处理:
- 日志文件
- 配置文件
- 代码文件
- 普通文本
时,BufferedReader 会更方便。
四、BufferedWriter 的特点
1. 基本使用
Writer fr = new FileWriter("day03-File-io/src/itheima/Demo11BufferWriter/xiaole-out2", true);
BufferedWriter br = new BufferedWriter(fr);
br.write('a');
br.write(98);
br.write("Java");
br.newLine();
char[] chars = "java".toCharArray();
br.write(chars);
2. 核心特点
(1)写文本效率更高
BufferedWriter 本质上就是在字符输出流的基础上增加缓冲机制,减少频繁写磁盘的次数,所以写文本时效率更高。
(2)支持多种写出方式
项目中演示了它可以:
- 写单个字符
- 写字符串
- 写字符数组
- 写字符串的一部分
- 写字符数组的一部分
这一点说明 BufferedWriter 在处理文本时非常灵活。
(3)支持 newLine()
项目里多次用到了:
br.newLine();
它的好处是:
- 不需要手动写
\r\n - 代码更清晰
- 可读性更好
所以在写多行文本时,BufferedWriter 会比普通写法更舒服。
(4)要注意刷新和关闭
缓冲输出流的数据不一定会立刻写入目标文件,它可能先停留在缓冲区中。
所以这里要注意两个方法:
flush():刷新缓冲区,但流还能继续使用close():关闭流,关闭前通常会自动刷新
开发中更推荐使用 try-with-resources,这样更安全。
五、缓冲字节流和缓冲字符流的区别
| 对比项 | 缓冲字节流 | 缓冲字符流 |
|---|---|---|
| 代表类 | BufferedInputStream BufferedOutputStream |
BufferedReader BufferedWriter |
| 处理单位 | 字节 | 字符 |
| 适用文件 | 所有文件 | 文本文件 |
| 是否适合图片/视频 | 适合 | 不适合 |
| 是否支持按行读取 | 不支持 | BufferedReader 支持 |
| 是否支持便捷换行 | 一般 | BufferedWriter.newLine() 很方便 |
| 核心优势 | 复制文件快 | 处理文本方便 |
一句话总结:
- 复制任意文件,优先考虑缓冲字节流
- 处理文本内容,优先考虑缓冲字符流
六、延伸:缓冲流和转换流经常配合使用
InputStream inputStream = new FileInputStream("day03-File-io\\src\\zifuxiaole3");
Reader isr = new InputStreamReader(inputStream, "GBK");
BufferedReader reader = new BufferedReader(isr);
这段代码说明在实际开发中,很多时候我们并不是直接:
FileReader + BufferedReader
而是:
InputStream + InputStreamReader + BufferedReader
这样做有两个好处:
- 指定编码,避免乱码
- 提高读取效率
所以可以总结一句经验:
缓冲流除了提升效率之外,还经常作为最外层包装流使用。
七、使用建议
结合这次内容,我总结了下面几点:
1. 复制文件时尽量用字节数组
byte[] buffer = new byte[1024 * 8];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
不要总是一个字节一个字节读写,效率太低。
2. 处理文本优先考虑缓冲字符流
因为它们:
- 读得快
- 写得方便
- 支持按行读取
- 支持便捷换行
3. 输出流一定要记得刷新和关闭
特别是缓冲输出流,如果没有正常关闭,数据可能还停留在缓冲区里。
总结
缓冲流并不是一种新的 IO 思路,而是在原有基础流之上增加了一层缓冲机制,让读写更加高效、更加方便。
最后再总结一下缓冲流最值得记住的几个特点:
BufferedInputStream、BufferedOutputStream适合处理任意文件BufferedReader、BufferedWriter适合处理文本文件BufferedReader支持按行读取BufferedWriter支持newLine()- 缓冲流本质上是包装流
- 真正常用的高效写法一般是 缓冲流 + 数组
如果刚开始学 IO,我觉得缓冲流这一块一定要多敲几遍,因为后面做文件复制、日志处理、配置文件读取时都会经常遇到。
更多推荐



所有评论(0)