深入理解:输入流read()方法的底层运作原理,以及为什么缓存空间可以极大的提升IO流读写文件的效率

深入理解:输入流read方法的底层运作原理,以及为什么缓存空间可以极大的提升读取文件的效率

一·FileInputStream类的read方法官方JDK解释如下图所示:可能文档说的太过官方,本人来阐述说明一下。

在这里插入图片描述

1.read():

在这里插入图片描述

(1)当输入流对象调用这个方法时,一次调用只会从某个文件中读取一字节(也就是8位)的二进制数据,然后返回读取的一字节数据。

(2)执行完一次调用之后,该方法并不会立即停掉,而是会进入阻塞状态。进程阻塞就是进程会在后台挂起,即,占着cpu内存,却不执行任何任务,但这样可以加快启动与调用的速度。
一旦这样的进程过多就会很耗费计算机各种资源,所以每次我们读完文件数据之后,都必须关掉输入输出流的原因就是这么回事。

(3)直到利用“同一个输入流对象”再次调用它,那么该read方法就会被唤醒,它就会接着从上次停止的地方继续执行读取操作。

2.read(byte [] b ):开发中常用

在这里插入图片描述

(1)当输入流对象调用这个方法时,该方法底层会一字节一字节的读取数据,每读取到一字节数据就会自动填充到缓存数组byte[]中去(从数组偏移量0开始),
直到填满缓存数组,该read方法就会返回本次调用填充缓存数组的总字节数。

(2)执行完一次调用之后,该方法并不会立即停掉,而是会进入阻塞状态。进程阻塞就是进程会在后台挂起,即,占着cpu内存,却不执行任何任务,但这样可以加快启动与调用的速度。
一旦这样的进程过多就会很耗费计算机各种资源,所以每次我们读完文件数据之后,都必须关掉输入输出流的原因就是这么回事。

(3)一般循环调用该方法读取文件数据时,不到文件末尾,每次读取的总字节数都会与缓存数组长度相等。
也就是说倒数第三次以上读取的文件总字节数,都等于缓存数组长度;
但是倒数第二次读取的总字节数就不一定等于缓存数组的长度,因为文件未被读取的数据中未必有足够的数据去填充缓存数组;
最后一次读取返回的才是-1,表示读取文件数据截止。

(4)直到利用“同一个输入流对象”再次调用它,那么该read方法就会被唤醒,它就会接着从上次停止的地方继续执行读取操作。

(5)注意:若最后一次调用该方法去读取文件的数据不够填满缓存数组,那么则会返回实际填充缓存数组的总字节数并进入阻塞状态;此时,再次调用该read方法,返回的才是-1,表示文件末尾截止了!!!

3.read( byte [] b , int off , int len):

在这里插入图片描述

(1)当输入流对象调用这个方法时,该方法底层会一字节一字节的读取数据,每读取到一字节数据就会自动填充到缓存数组byte[]中去(从数组偏移量off开始),
一次方法调用中最多填充len字节数到缓存数组中,该read方法就会返回本次调用填充缓存数组的总字节数。

(2)执行完一次调用之后,该方法并不会立即停掉,而是会进入阻塞状态。进程阻塞就是进程会在后台挂起,即,占着cpu内存,却不执行任何任务,但这样可以加快启动与调用的速度。
一旦这样的进程过多就会很耗费计算机各种资源,所以每次我们读完文件数据之后,都必须关掉输入输出流的原因就是这么回事。

(3)直到利用“同一个输入流对象”再次调用它,那么该read方法就会被唤醒,它就会接着从上次停止的地方继续执行读取操作。

(4)如果off为负数,或者len为负数,也或者 len为大于 b.length - off 的数,这个方法运行时都会报错

二·FileReader类都是继承实现的Reader基类的read方法,官方JDK解释如下图所示:可能文档说的太过官方,本人来阐述说明一下。

在这里插入图片描述

1.read():

在这里插入图片描述

(1)当输入流对象调用这个方法时,一次调用只会从某个文件中读取一字符的二进制数据,然后返回读取的一字符数据。
注意:若字符是英文,则是读取一字节的数据;若字符是汉字,则是读取2字节的数据;返回的是二进制数据,显示在控制台会转换为int类型数据,
只要将这个int类型数据赋值给char类型数据再进行输出就可以成功转换为字符了。

(2)执行完一次调用之后,该方法并不会立即停掉,而是会进入阻塞状态。进程阻塞就是进程会在后台挂起,即,占着cpu内存,却不执行任何任务,但这样可以加快启动与调用的速度。
一旦这样的进程过多就会很耗费计算机各种资源,所以每次我们读完文件数据之后,都必须关掉输入输出流的原因就是这么回事。

(3)直到利用“同一个输入流对象”再次调用它,那么该read方法就会被唤醒,它就会接着从上次停止的地方继续执行读取操作。

2.read​(char[] cbuf):开发中常用

在这里插入图片描述

(1)当输入流对象调用这个方法时,该方法底层会一字符一字符的读取数据,每读取到一字符数据就会自动填充到缓存数组char[]中去(从数组偏移量0开始),
直到填满缓存数组,该read方法就会返回本次调用填充缓存数组的总字符数。

(2)执行完一次调用之后,该方法并不会立即停掉,而是会进入阻塞状态。进程阻塞就是进程会在后台挂起,即,占着cpu内存,却不执行任何任务,但这样可以加快启动与调用的速度。
一旦这样的进程过多就会很耗费计算机各种资源,所以每次我们读完文件数据之后,都必须关掉输入输出流的原因就是这么回事。

(3)一般循环调用该方法读取文件数据时,不到文件末尾,每次读取的总字符数都会与缓存数组长度相等。
也就是说倒数第三次以上读取的文件总字符数,都等于缓存数组长度;
但是倒数第二次读取的总字符数就不一定等于缓存数组的长度,因为文件未被读取数据已经没了;最后一次读取返回的才是-1,表示读取文件数据截止。

(4)直到利用“同一个输入流对象”再次调用它,那么该read方法就会被唤醒,它就会接着从上次停止的地方继续执行读取操作。

(5)注意:若最后一次调用该方法去读取文件的数据不够填满缓存数组,那么则会返回实际填充缓存数组的总字符数并进入阻塞状态;此时,再次调用该read方法,返回的才是-1,表示文件末尾截止了!!!

3.read​(char[] cbuf, int off, int len):

在这里插入图片描述

(1)当输入流对象调用这个方法时,该方法底层会一字符一字符的读取数据,每读取到一字符数据就会自动填充到缓存数组char[]中去(从数组偏移量off开始),
一次方法调用中最多填充len字符数到缓存数组中,该read方法就会返回本次调用填充缓存数组的总字符数。

(2)执行完一次调用之后,该方法并不会立即停掉,而是会进入阻塞状态。进程阻塞就是进程会在后台挂起,即,占着cpu内存,却不执行任何任务,但这样可以加快启动与调用的速度。
一旦这样的进程过多就会很耗费计算机各种资源,所以每次我们读完文件数据之后,都必须关掉输入输出流的原因就是这么回事。

(3)直到利用“同一个输入流对象”再次调用它,那么该read方法就会被唤醒,它就会接着从上次停止的地方继续执行读取操作。

(4)如果off为负数,或者len为负数,也或者 len为大于 b.length - off 的数,这个方法运行时都会报错

三·为什么缓存空间可以极大的提升IO流读写文件的效率,又是如何提升系统的整体性能呢?

1.前提共识:不需要修改原文件数据的操作就是输入流,需要修改某文件数据的操作就是输出流。

(1)从磁盘往cpu内存读取数据的速度为v1
(2)从cpu内存往程序里面读取数据的速度为v2
(3)从程序里面往cpu内存写数据的速度为v3
(4)从cpu内存往磁盘写数据为v4

结论:v2 = v3,v1 = v4,v2 >> v1,v3 >> v4

2.缓存空间节省IO流操作文件时间的过程,可以通过一个比较直观形象的比喻来阐述说明,如下图所示:在文件大小不变的情况下,缓存空间就相当于提升了读写文件的效率。

在这里插入图片描述

3.输出流一次写出一个字节数据,与一次写出很多个字节数据,耗费的时间几乎相差不大。

四·注意事项:

1.缓存数组,又称“缓冲数组”

2.官方文档中“直到某些输入可用”的词语解释:直到利用同一个输入流对象再次调用该read方法

3.调用read(byte[] b)以及read( byte [] b , int off , int len)方法时,传入的缓存字节数组byte[]大小,可以随便设置长度,但长度不能小于1。

4.缓冲数组可以极大的提升IO流读写文件的效率,当很多缓存空间都发挥如此作用的时候,也就提升了系统的整体性能。

5.当read方法读取数据返回的是-1时,此时read方法虽然会停掉,但是输入流进程仍然没有关闭,必须执行close方法才能关闭流的进程。

6.其他输入流的read方法理解本质上与上文两种都差不多,类推就行

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是用字节缓冲实现二进制文件读写操作的Java代码,可以将wn.mp3文件复制为hl.mp3文件: ```java import java.io.*; public class BinaryFileCopy { public static void main(String[] args) { String sourceFileName = "wn.mp3"; // 原始文件名 String destinationFileName = "hl.mp3"; // 目标文件名 int bufferSize = 1024 * 1024; // 缓冲区大小 try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFileName), bufferSize); OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(destinationFileName), bufferSize)) { byte[] buffer = new byte[bufferSize]; int len; while ((len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } outputStream.flush(); System.out.println("文件复制成功!"); } catch (IOException e) { e.printStackTrace(); } } } ``` 上述代码中,我们使用了字节缓冲输入BufferedInputStream和字节缓冲输出BufferedOutputStream,这两个都是装饰器,它们可以在内部维护一个缓冲区,可以提高读写效率。在使用这两个时,我们需要提供一个缓冲区大小,这里我们设置为1MB。 在读写文件时,我们使用了一个byte数组作为缓存,每次从输入中读取数据,然后写入到输出中,直到读取完毕。最后,我们需要调用输出的flush()方法,将缓存中的数据刷入文件中,确保文件的完整性。 注意:这里我们使用了Java 7中引入的try-with-resources语句,它可以自动关闭,无需手动关闭。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ideal-cs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值