BufferedInputStream如何缓冲IO以及InputStream中的read(byte[] b) 是否具有缓冲功能?

今天做IO练习的时候,突然意识到InputStream本身提供了3个read方法。其中有2个是将字节码读到一个byte数组中,那这个方法不是和BuffereInputStream一样提供了缓冲功能了么?

    翻了翻api,看了看源码,google了半天,只有这兄弟说得靠谱,转载了过来。原文地址:点击打开链接

从顶级的InputStream开始 

InputStream 定义了3个read方法。 

Java代码   收藏代码
  1. read();  
  2. read(byte[]);  
  3. read(byte[],int off,int len);  

第二个read(byte[])其实就是read(b, 0, b.length) ,所以等同于第三个; 

第一个read()方法,api介绍如下: 
引用
从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 
子类必须提供此方法的一个实现。


第三个read()方法,api介绍如下: 
引用
将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。 
在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 

如果 len 为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。 

将读取的第一个字节存储在元素 b[off] 中,下一个存储在 b[off+1] 中,依次类推。读取的字节数最多等于 len。设 k 为实际读取的字节数;这些字节将存储在 b[off] 到 b[off+k-1] 的元素中,不影响 b[off+k] 到 b[off+len-1] 的元素。 

在任何情况下,b[0] 到 b[off] 的元素以及 b[off+len] 到 b[b.length-1] 的元素都不会受到影响。 

类 InputStream 的 read(b, off, len) 方法重复调用方法 read()。如果第一次这样的调用导致 IOException,则从对 read(b, off, len) 方法的调用中返回该异常。如果对 read() 的任何后续调用导致 IOException,则捕获该异常并将其视为到达文件末尾;到达该点时读取的字节存储在 b 中,并返回发生异常之前读取的字节数。在已读取输入数据 len 的请求数量、检测到文件结束标记、抛出异常前,此方法的默认实现将一直阻塞。 建议子类提供此方法更为有效的实现。


关于三段红字的注解: 
第一段:InputStream是所有输入流的顶级类,当然只定义,不实现,具体的由子类去实现,如AudioInputStream, ByteArrayInputStream, FileInputStream等。 
第二段:指明了InputStream的read(byte[],int off,int len)的实现方式,就是简单的调用read()方法而已,而read()方法是一次只读取一个字节,依然每次都要调用底层系统,所以InputStream的read(byte[],int off,int len)性能和直接调用read()一样,byte[]缓冲区在这是摆设。 
第三段:正是由于第二段所说,才建议子类提供性能更好的方式来覆盖read(byte[],int off,int len)方法。 

那InputStream的子类有哪些呢?看下API就知道了,这几只拿FileInputStream来说. 
下面是FileInputStream的部分源码: 
Java代码   收藏代码
  1. public native int read() throws IOException;  
  2.   
  3. private native int readBytes(byte b[], int off, int len) throws IOException;  
  4.   
  5. public int read(byte b[]) throws IOException {  
  6. return readBytes(b, 0, b.length);  
  7.    }  
  8.   
  9. public int read(byte b[], int off, int len) throws IOException {  
  10. return readBytes(b, off, len);  
  11.    }  

这里两个read()方法都是用本地方法实现,因为FileInputStream是跟底层的操作系统交互的,没有比用本地方法来实现的性能更好,更容易的了。所以这里就采用了第三段里的建议,真正实现了缓存的功能,虽然我们并不知道如何实现的。 

那么既然FileInputStream已经实现了缓存来提高性能,那么BufferedInputStream又拿来干嘛? 
先看api介绍: 
引用
BufferedInputStream 为另一个输入流添加一些功能,即 缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。


其实上面所说的“缓冲输入”并不是真正的像FileInputStream那样用本地方法来提高性能,而是指在这基础上,为了程序员操作方便,内部提供了一个缓冲区(byte[1024*8] buf),并装饰了FileInputStream类(构造BufferedInputStram时必须提供被装饰的InputStream就可看出)。 
当用FileInputStream的时候,read()是从底层读一个字节,read(byte[],int off,int len)则是一次性读取了len-off个字节,我们需提供一个byte[]来存放, 
而用BufferedInputStream的时候,其read()其实和read(byte[],int off,int len)一样,内部都是调用构造输入的FileInputStream的read(byte[],int off,int len)方法,将底层数据读入到byte[]里,而且byte[]不需要我们来提供,类本身定义了一个byte[] buf数组来存放这些数据,所以,如果使用BufferedInputStream我们的程序又不需要对byte[]数组操作的话,直接这样写就行了: 
Java代码   收藏代码
  1. FileInputStream fis=new FileInputStream("d:\\a.txt");  
  2. BufferedInputStream bis=new BufferedInputStream(fis);  
  3. int data=0;  
  4. while((data=bis.read())!=-1){  
  5.     //......          
  6. }  

这样虽然也是一次读一个字节,但不是每次都从底层读取数据,而是一次调用底层系统读取了最多buf.length个字节到buf数组中,然后从 buf中 一次读一个字节,减少了频繁调用底层接口的开销。 
等同于  
Java代码   收藏代码
  1. FileInputStream fis=new FileInputStream("d:\\a.txt");  
  2. byte[] mybuff=new byte[1024];  
  3. int count=0;  
  4. while((count=fis.read(mybuff))!=-1){  
  5.      //......  
  6. }  


如果是用BufferedInputStream的read(byte[],int off,int len)那缓冲区则由传入的byte[]来充当(虽然内部其实有时候还用到了buf,但表现出来的就是用传入的byte[]来缓冲)。 

讲了这么多,那如果要缓冲那该用FileInputStream还是BufferedInputStream呢?回到上面紫色的文字,BufferedInputStream主要不是提供buf,而是封装了缓冲和标记/回读的功能。如果你既不用到标记/回读功能,又不要操作中间的缓冲数组,那显而易见直接用FileInputStream的read(byte[],int off,int len)是效率最高的。 

最后说下为什么用缓冲性能就更好,因为应用程序可以将多个字节写入底层输出流中(native read(byte)),而不必针对每个字节写入都调用底层系统(native read())。OutputStream原理基本差不多,这里就不说了。


原文中只用了FileInputStream举例,自己又翻了翻网络中常用的HttpInputStream,发现这个类只是和父类一样read(byte[] b)调用了read()方法,并没用改写为native方法,那是不是意味着这样的情况下,BufferedInputStream包裹在外面一样是一次次的调用底层的read()方法填充到内置的buff数组中中?那这样的缓冲实在是意义有限……

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
以下是一个完整的示例代码,演示如何使用 `BufferedInputStream` 增加缓冲区来读取数据: ```java import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class BufferedInputStreamExample { public static void main(String[] args) { try { InputStream inputStream = new FileInputStream("example.txt"); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); byte[] buffer = new byte[8192]; // 缓冲区大小为 8192 字节 int bytesRead; while ((bytesRead = bufferedInputStream.read(buffer)) != -1) { // 处理读取到的数据,buffer 数组bytesRead 个字节是有效数据 // 这里可以根据需要进行操作,例如写入到其他输出流或进行数据处理 } bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 在上面的代码,我们首先创建了一个 `FileInputStream` 对象来读取文件 "example.txt"。然后,我们使用 `BufferedInputStream` 对象包装该输入流,增加了缓冲区。 在 `while` 循环,我们使用 `bufferedInputStream.read(buffer)` 方法来读取数据。该方法会将最多 `buffer.length` 个字节的数据读入 `buffer` 数组,并返回实际读取的字节数。当返回值为 -1 时,表示已经读到了流的末尾。 您可以在循环对读取到的数据进行处理,例如写入到其他输出流或进行进一步的数据处理操作。 最后,不要忘记在使用完毕后关闭流对象以释放资源。在示例代码,我们调用了 `bufferedInputStream.close()` 方法来关闭流。 请注意,根据实际需求,您可能需要适当调整缓冲区的大小。过小的缓冲区可能导致频繁的 I/O 操作,过大的缓冲区则可能占用过多的内存。因此,根据具体情况进行测试和调整是很重要的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值