6.BufferedInputStream 缓冲输入字节流
1.BufferedInputStream:
通过使用FileInputStream我们可以知道,使用缓冲数组能提高读取效率。因此sun给我们提供了一个缓冲输入字节流对象,让我们可以更高效的读取文件。
2.输入字节流体系
- - - - | InputStream 输入字节流的基类,抽象类
- - - - - - - - - | FileInputStream 读取文件数据的输入字节流(直接从硬盘读取),每次读取一个字节
- - - - - - - - - | BufferedInputStream 缓冲输入字节流,它的出现主要是为了提高读取文件数据的效率。其实BufferedInputStream只不过是在内部维护了一个8192字节(8kb)的字节数组而已。
3.使用BufferedInputStream的步骤:
1.找到目标文件
File file = new File("E:\\aa\\a.txt");
2.建立数据通道
BufferedInputStream bufferedInputStream = new BufferedInputStream(newFileInputStream(file));
3.读取数据
int content = bufferedInputStream.read();// 借用的是FileInputStream的read()方法的读取文件数据的能力,所有缓冲字节流都不具备读写文件的功能。
4.释放流资源
bufferedInputStream.close();// (实际上关闭的是 FileInputStream的资源。)
4.关于 BufferedInputStream 的问题:
(1)为什么创建BufferedInputStream对象的时候,要传入FileInputStream对象?
答:因为所有的缓冲字节流都不具备读写文件的能力。所以BufferedInputStream不具备读写文件的能力,不能直接从硬盘中读取文件中的数据。需要借助FileInputStream的read()的读取文件数据的能力来完成文件数据的读取操作。
(2)BufferedInputStream出现的目的是为了提高读取数据的效率,但是BufferedInputStream每次读取一个字节的数据,而FileInputStream也是每次读写一个字节的数据,那么BufferedInputStream的高效率从何而来呢?
答:BufferedInputStream只不过是在类的内部维护了一个8192字节(8kb)的字节数组而已。
从BufferedInputStream的源码看它的运行原理:
BufferedInputStream的源码: public synchronized int read() throws IOException { if (pos >= count) { fill(); if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; } |
源码解读: pos:当前已经读取到了第几个字节(指针作用) count:本次读取了几个字节存储到了缓冲数组中。 getBufIfOpen():获取字节数组中的数据。 fill():填充。获取getBufIfOpen()获取到的字节数组中的数据。使用BufferedInputStream内部维护的数组去读取文件数据,每次最多读取8kb(8192字节)的数据进入缓冲数组。 |
通过源码可以得知BufferedInputStream缓冲字节输入流的运行原理: BufferedInputStream 是从字节数组(也就是内存中)读取数据的,它没有读写文件的能力。 第一次读取时pos和count都是0,此时进入if内部调用 fill() 方法,fill() 维护了 getBufIfOpen() 方法读取的数据(每次最多8kb)。 fill() 方法起到一个指针的作用,每读取完一个字节指针索引值+1,直到pos>=count,return -1读取完字节数组中的所有数据,然后pos指针清零,继续下一轮的读取操作,直到将文件中的所有数据都读取完成。 |
(3)当使用完流资源,要第一时间将流资源关闭,为什么只关闭BufferedInputStream的流资源,而不关闭FileInputStream的流资源?
答:关闭BufferedInputStream的资源,实际上关闭的就是FileInputStream的资源,因为BufferedInputStream不具备读取文件的能力,所以不需要释放资源。而它读取文件的能力是借助的FileInputStream的read()方法,所以关闭的实际上是FIleInputStream的资源。
5.BufferedInputStream与FileInputStream的使用选取问题?
看个人习惯,总的来说,使用FileInputStream搭配数组的效率应该比BufferedInputStream的效率要高。
从以下几点可以分析出:
1.执行流程:从BufferedInputStream的源码可以看出,每次读取字节都需要进行if判断,这就很影响工作效率了。
2.程序员所需写的代码量:两种字节输入流使用时,所需的代码量是相同的。只不过FileInputStream需要自己去维护一个byte数组而BufferedInputStream却需要将FileInputStream传入它构建的对象中。
3.维护数组:BufferedInputStream内部维护的数组是8kb(8192个字节),也就是1024*8个字节,是固定的。而程序员自己去维护数组的话,会更加灵活。
6.案例
public class Dome1 {
/*
3.使用BufferedInputStream的步骤:
1.找到目标文件
2.建立数据通道
3.读取数据
4.释放流资源
*/
public static void main(String[] args) {
// 1.找到目标文件
File file = new File("E:\\aa\\bb\\a.txt");
FileInputStream fileInputStream = null;
BufferedInputStream bufferedInputStream = null;
try {
// 2.搭建数据通道
// 疑问1:问什么在创建BufferedInputStream的时候,要传入FileInputStream? 因为BufferedInputStream(所有缓冲字节流)不具备读写文件的能力,需要借助FileInputStream的read()方法读取文件数据内容。
fileInputStream = new FileInputStream(file);
bufferedInputStream = new BufferedInputStream(fileInputStream);
// 3.读取数据
/*
缓冲字节输入流的运行原理:BufferedInputStream只不过是在类的内部维护了一个8192字节(8kb)的字节数组而已。
BufferedInputStream源码:
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
pos:当前已经读取到了第几个字节(指针作用)
count:本次读取了几个字节存储到了缓冲数组中。
getBufIfOpen():获取字节数组中的数据。
fill():填充。获取getBufIfOpen()获取到的字节数组中的数据。使用BufferedInputStream内部维护的数组去读取文件数据,每次最多读取8kb(8192字节)的数据进入缓冲数组。
通过源码可以得知BufferedInputStream缓冲字节输入流的运行原理:
BufferedInputStream是从字节数组(也就是内存中)读取数据的,
第一次读取时pos和count都是0,此时进入if内部调用fill()方法,fill()维护getBufIfOpen()方法一次性读取的最多8kb的数据。
fill()方法起到一个指针的作用,每读取完一个字节指针索引值+1,直到pos>=count,return -1读取完文件中的所有数据。
*/
int content = 0;
while ((content = bufferedInputStream.read()) != -1) {
System.out.println((char) content);
}
} catch (FileNotFoundException e) {
System.out.println("寻找目标文件失败...");
throw new RuntimeException(e);// 将异常封装到运行时异常处理掉
} catch (IOException e) {
System.out.println("读取文件内容失败...");
throw new RuntimeException(e);
} finally {
try {
// 4.释放资源
System.out.println("关闭流资源成功......");
bufferedInputStream.close();//实际上释放的是FileInputStream的资源,因为BufferedInputStream不具备读写文件的能力,所以不能搭建与文件之间的通道,它借助的是FileInputStream的read()方法来读写文件的数据。
} catch (IOException e) {
System.out.println("关闭流资源失败......");
throw new RuntimeException(e);
}
}
}
}
总结:简单来说,BufferedInputStream读取文件的过程,是通过FileInputStream的read()方法,将硬盘中文件的数据内容读取到BufferedInputStream内部维护的字节数组中,此时数据存储到了内存中,BufferedInputStream的read()方法是从字节数组(也就是内存中)来读取数据的。这也就是为什么BufferedInputStream明明不具备读写文件的功能,却能读取文件中数据的原因。