

整理下以前自学的笔记,留个念想,不喜轻喷。希望基础不好的同学看完这篇文章,能掌握缓冲流,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆。
想象一个场景:我们在电脑上编辑文件,可以保存到硬盘上,也可以拷贝到 U 盘中。那这个看似简单的过程,背后其实是数据的传输。
数据的传输,也就是数据的流动。既然是流动也就会有方向,有入方向和出方向。举个上传文件的栗子,现在有三个对象,文件、应用程序、上传的目标地址(服务器)。简化的上传文件有两步:
注意这个入和出是相对的,它相对于应用程序而言。如果相对于服务器而言,这个上传文件操作就是入方向,从应用程序读入。Java 中 I/O 操作主要是指使用 java.io 包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
我不认同网络上很多 IO 流的图,他们只是简单的把 io 流分成字节流和字符流。这样的分类也不是说不好,只是太臃肿、难记。
先上一张我自己总结的 IO 留的思维导图,我先把它分成了节点流和处理流,节点流是直接接触数据源的,而处理流是出于各种目的在节点流的基础上再套一层的 IO 流。再按照操作类型,分成 8 个小类,然后才是字节、字符分类,最后才是输入、输出的分类。具体可以看以下思维导图 (可能不清晰,有需要的在后台回复 IO 流获取原思维导图)
根据数据的流向分为:输入流和输出流。
其他设备上读取到内存中的流。内存 中写出到其他设备上的流。根据数据的类型分为:字节流和字符流。

Java IO 流
IO 流要说明白需要好几篇才行,今天复习缓冲流。
前面我们已经复习过字节流、字符流。使用基本的字节输入流读取文件,就相当于将文件中的数据,通过操作系统,在通过 JVM 一个个传入到内存中,这样的话,文件读取的速度比较慢。如果使用字节缓冲流,就可以建立一个缓冲区(相当于一个数组),将缓冲区里面的数据批量传入到文件中,这样的话就提高了文件的读取速度。一句话概括就是:缓冲流比较高效,因为它减少了 IO 的次数。
缓冲流,也叫高效流,是对 4 个基本的字节、字符流的增强,所以也是 4 个流,按照数据类型分类:
BufferedInputStream,BufferedOutputStreamBufferedReader,BufferedWriter它的基本原理是:会在创建流的时候创建一个默认大小的内置缓冲区,从而减少文件系统 IO 次数,提高效率。
字节缓冲流与文件字节流的用法差不多不再赘述,有一点不同的是字节缓冲流的创建是建立在文件字节流的基础上,这就导致构造方法的变化,字节缓冲流的构造方法是这样的:
// 字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Z:\\唐顿庄园BD中英双字.mp4"));
// 字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Z:\\movie\\复制唐顿庄园BD中英双字.mp4"));
上面说到缓冲流比普通的字节流效率要高,为了验证,我们来做个测试,分别用缓冲流和文件字节流复制同样大小的文件(1.3GB)到相同的目录。
首先是文件字节流复制:
/**
* Project Name:review_java <br/>
* Package Name:com.nasus.io.bufferinoutstream <br/>
* Date:2020/2/17 22:41 <br/>
*
* @author <a href="turodog@foxmail.com">chenzy</a><br/>
*/
public class CopyFileByByteStream {
public static void main(String[] args) throws FileNotFoundException {
// 开始时间
long startTime = System.currentTimeMillis();
// 计数器,用于判断
int b;
try {
// 创建字节流、复制电影
FileInputStream fis = new FileInputStream("Z:\\唐顿庄园BD中英双字.mp4");
FileOutputStream fos = new FileOutputStream("Z:\\movie\\复制唐顿庄园BD中英双字.mp4");
// 读写数据
while ((b = fis.read()) != -1) {
fos.write(b);
}
// 关闭资源
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
// 结束时间
long endTime = System.currentTimeMillis();
// 统计用时
System.out.println("普通流复制时间:" + (endTime - startTime) + " 毫秒");
}
}
我等了十分钟,看了一下复制过去的文件大小只有 80M 不到,最终时间等不下去了。。。足以见它的效率有多么低。
接着是缓冲流复制文件:
/**
* Project Name:review_java <br/>
* Package Name:com.nasus.io.bufferinoutstream <br/>
* Date:2020/2/17 22:56 <br/>
*
* @author <a href="turodog@foxmail.com">chenzy</a><br/>
*/
public class CopyFileByBufferStream {
public static void main(String[] args) throws FileNotFoundException {
// 开始时间
long startTime = System.currentTimeMillis();
// 计数器,用于判断
int b;
try {
// 创建缓冲流,复制电影
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Z:\\唐顿庄园BD中英双字.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Z:\\movie\\复制唐顿庄园BD中英双字.mp4"));
// 读写数据
while ((b = bis.read()) != -1) {
bos.write(b);
}
// 关闭资源
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
// 结束时间
long endTime = System.currentTimeMillis();
// 统计用时
System.out.println("缓冲流(默认缓冲区)复制时间:" + (endTime - startTime) + " 毫秒");
}
}
下图为证,1.3 GB 的文件用了 46.7 秒复制完成。

缓冲流(不用数组)复制时间
最后是缓冲流使用数组(一次读多一点):
/**
* Project Name:review_java <br/>
* Package Name:com.nasus.io.bufferinoutstream <br/>
* Date:2020/2/17 23:03 <br/>
*
* @author <a href="turodog@foxmail.com">chenzy</a><br/>
*/
public class CopyFileByBufferArray {
public static void main(String[] args) throws FileNotFoundException {
// 开始时间
long startTime = System.currentTimeMillis();
// 计数器,用于判断
int b;
// 使用数组,一次读多点
byte[] bytes = new byte[8*1024];
try {
// 创建缓冲流,复制电影
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Z:\\唐顿庄园BD中英双字.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Z:\\movie\\复制唐顿庄园BD中英双字.mp4"));
// 读写数据
while ((b = bis.read(bytes)) != -1) {
bos.write(bytes, 0 , b);
}
// 关闭资源
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
// 结束时间
long endTime = System.currentTimeMillis();
// 统计用时
System.out.println("缓冲流(使用数组)复制时间:" + (endTime - startTime) + " 毫秒");
}
}
下入为证,1.3GB 的文件用了 2.3 秒复制完成。

缓冲流(使用数组)复制时间
与字节缓冲流一样,字符缓冲流的创建也是建立在文件字符流的基础上:
// 字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
字符缓冲流的大部分方法跟文件字符流一样,这里需要说一下的就是 readLine() 和 newLine() 方法:
BufferedReader:public String readLine(): 读一行文字。
BufferedWriter:public void newLine(): 写一行行分隔符(回车),由系统属性定义符号。
// 各系统的符号以及对应关系如下:
// windows --> \r\n
// linux --> \r
// mac --> \n
// \r 是回车,return
// \n 是换行,newline
/**
* Project Name:review_java <br/>
* Package Name:com.nasus.io.bufferinoutstream <br/>
* Date:2020/2/17 23:23 <br/>
*
* @author <a href="turodog@foxmail.com">chenzy</a><br/>
*/
public class BufferReaderDemo {
public static void main(String[] args) throws IOException {
// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 定义字符串,保存读取的一行文字
String line = null;
// 循环读取,读取到最后返回null
while ((line = br.readLine()) != null) {
System.out.println(line);
System.out.println("------");
}
// 释放资源
br.close();
}
}
/**
* Project Name:review_java <br/>
* Package Name:com.nasus.io.bufferinoutstream <br/>
* Date:2020/2/17 23:24 <br/>
*
* @author <a href="turodog@foxmail.com">chenzy</a><br/>
*/
public class BufferWriterDemo {
public static void main(String[] args) throws IOException {
// 创建流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
// 写出数据
bw.write("一个");
// 写出换行
bw.newLine();
bw.write("优秀");
bw.newLine();
bw.write("废人");
bw.newLine();
// 释放资源
bw.close();
}
}
结果:

写入换行符
一开始文件是每行打乱序号的,如下:
4.废人
1.czy
3.优秀的
2.是一个
/**
* Project Name:review_java <br/>
* Package Name:com.nasus.io.bufferinoutstream <br/>
* Date:2020/2/17 23:37 <br/>
*
* @author <a href="turodog@foxmail.com">chenzy</a><br/>
*/
public class BufferSortTest {
public static void main(String[] args) throws IOException {
// 创建map集合,保存文本数据,键为序号,值为文字
HashMap<String, String> lineMap = new HashMap<>();
// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("乱序.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("正序.txt"));
// 读取数据
String line = null;
while ((line = br.readLine())!=null) {
// 解析文本
String[] split = line.split("\\.");
// 保存到集合
lineMap.put(split[0],split[1]);
}
// 关闭资源
br.close();
// 遍历map集合
for (int i = 1; i <= lineMap.size(); i++) {
String key = String.valueOf(i);
// 获取map中文本
String value = lineMap.get(key);
// 写出拼接文本
bw.write(key+"."+value);
// 写出换行
bw.newLine();
}
// 关闭资源
bw.close();
}
}
结果:
1.czy
2.是一个
3.优秀的
4.废人
https://github.com/turoDog/review_java
-END-