首页 > Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/54894451

本文出自【赵彦军的博客】

InputStream
|__FilterInputStream|__BufferedInputStream

首先抛出一个问题,有了InputStream为什么还要有BufferedInputStream?

BufferedInputStreamBufferedOutputStream这两个类分别是FilterInputStreamFilterOutputStream的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。

我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!

同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReaderBufferedWriter两个类。

现在就可以回答在本文的开头提出的问题:

BufferedInputStreamBufferedOutputStream类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。

总结:

  • BufferedInputStream 是缓冲输入流。它继承于FilterInputStream

  • BufferedInputStream 的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持mark()标记reset()重置方法

  • BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。

BufferedInputStream API简介

源码关键字段分析


private static int defaultBufferSize = 8192;//内置缓存字节数组的大小 8KBprotected volatile byte buf[];  //内置缓存字节数组protected int count;    //当前buf中的字节总数、注意不是底层字节输入流的源中字节总数protected int pos;      //当前buf中下一个被读取的字节下标protected int markpos = -1; //最后一次调用mark(int readLimit)方法记录的buf中下一个被读取的字节的位置protected int marklimit;    //调用mark后、在后续调用reset()方法失败之前云寻的从in中读取的最大数据量、用于限制被标记后buffer的最大值

构造函数

BufferedInputStream(InputStream in) //使用默认buf大小、底层字节输入流构建bis BufferedInputStream(InputStream in, int size) //使用指定buf大小、底层字节输入流构建bis  

一般方法介绍

int available();  //返回底层流对应的源中有效可供读取的字节数      void close();  //关闭此流、释放与此流有关的所有资源  boolean markSupport();  //查看此流是否支持markvoid mark(int readLimit); //标记当前buf中读取下一个字节的下标  int read();  //读取buf中下一个字节  int read(byte[] b, int off, int len);  //读取buf中下一个字节  void reset();   //重置最后一次调用mark标记的buf中的位子  long skip(long n);  //跳过n个字节、 不仅仅是buf中的有效字节、也包括in的源中的字节 

BufferedOutputStream API简介

关键字段

protected byte[] buf;   //内置缓存字节数组、用于存放程序要写入out的字节  protected int count;   //内置缓存字节数组中现有字节总数 

构造函数

BufferedOutputStream(OutputStream out); //使用默认大小、底层字节输出流构造bos。默认缓冲大小是 8192 字节( 8KB )BufferedOutputStream(OutputStream out, int size);  //使用指定大小、底层字节输出流构造bos  

构造函数源码:

/*** Creates a new buffered output stream to write data to the* specified underlying output stream.* @param   out   the underlying output stream.*/public BufferedOutputStream(OutputStream out) {this(out, 8192);}/*** Creates a new buffered output stream to write data to the* specified underlying output stream with the specified buffer* size.** @param   out    the underlying output stream.* @param   size   the buffer size.* @exception IllegalArgumentException if size <= 0.*/public BufferedOutputStream(OutputStream out, int size) {super(out);if (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");}buf = new byte[size];}

一般方法

//在这里提一句,`BufferedOutputStream`没有自己的`close`方法,当他调用父类`FilterOutputStrem`的方法关闭时,会间接调用自己实现的`flush`方法将buf中残存的字节flush到out中,再`out.flush()`到目的地中,DataOutputStream也是如此。 void  flush();  将写入bos中的数据flush到out指定的目的地中、注意这里不是flush到out中、因为其内部又调用了out.flush()  write(byte b);      将一个字节写入到buf中  write(byte[] b, int off, int len);      将b的一部分写入buf中 

那么什么时候flush()才有效呢?

答案是:当OutputStream是BufferedOutputStream时。

当写文件需要flush()的效果时,需要

FileOutputStream fos = new FileOutputStream("c:a.txt");

BufferedOutputStream bos = new BufferedOutputStream(fos);

也就是说,需要将FileOutputStream作为BufferedOutputStream构造函数的参数传入,然后对BufferedOutputStream进行写入操作,才能利用缓冲及flush()。

查看BufferedOutputStream的源代码,发现所谓的buffer其实就是一个byte[]。

BufferedOutputStream的每一次write其实是将内容写入byte[],当buffer容量到达上限时,会触发真正的磁盘写入。

而另一种触发磁盘写入的办法就是调用flush()了。

1.BufferedOutputStreamclose()时会自动flush

2.BufferedOutputStream在不调用close()的情况下,缓冲区不满,又需要把缓冲区的内容写入到文件或通过网络发送到别的机器时,才需要调用flush.

实战演练1:复制文件.

操作:使用缓存流将F盘根目录里面名字为:123.png 图片复制成 abc.png

package com.app;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;public class A3 {public static void main(String[] args) throws IOException {String filePath = "F:/123.png" ;String filePath2 = "F:/abc.png" ;File file = new File( filePath ) ;File file2 = new File( filePath2 ) ;copyFile( file , file2 );}/*** 复制文件* @param oldFile* @param newFile*/public static void copyFile( File oldFile , File newFile){InputStream inputStream = null ;BufferedInputStream bufferedInputStream = null ;OutputStream outputStream = null ;BufferedOutputStream bufferedOutputStream = null ;try {inputStream = new FileInputStream( oldFile ) ;bufferedInputStream = new BufferedInputStream( inputStream ) ;outputStream = new FileOutputStream( newFile ) ;bufferedOutputStream = new BufferedOutputStream( outputStream ) ;byte[] b=new byte[1024];   //代表一次最多读取1KB的内容int length = 0 ; //代表实际读取的字节数while( (length = bufferedInputStream.read( b ) )!= -1 ){//length 代表实际读取的字节数bufferedOutputStream.write(b, 0, length );}//缓冲区的内容写入到文件bufferedOutputStream.flush();} catch (FileNotFoundException e) {e.printStackTrace();}catch (IOException e) {e.printStackTrace();}finally {if( bufferedOutputStream != null ){try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if( bufferedInputStream != null){try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}if( inputStream != null ){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if ( outputStream != null ) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}
}

效果图:

这里写图片描述

如何正确的关闭流

在上面的代码中,我们关闭流的代码是这样写的。

finally {if( bufferedOutputStream != null ){try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if( bufferedInputStream != null){try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}if( inputStream != null ){try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if ( outputStream != null ) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}

思考:在处理流关闭完成后,我们还需要关闭节点流吗?

让我们带着问题去看源码:

  • bufferedOutputStream.close();
   /*** Closes this input stream and releases any system resources* associated with the stream.* Once the stream has been closed, further read(), available(), reset(),* or skip() invocations will throw an IOException.* Closing a previously closed stream has no effect.** @exception  IOException  if an I/O error occurs.*/public void close() throws IOException {byte[] buffer;while ( (buffer = buf) != null) {if (bufUpdater.compareAndSet(this, buffer, null)) {InputStream input = in;in = null;if (input != null)input.close();return;}// Else retry in case a new buf was CASed in fill()}}

close()方法的作用

1、关闭输入流,并且释放系统资源

2、BufferedInputStream装饰一个 InputStream 使之具有缓冲功能,is要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会调用真正数据源对象的 close()方法。因此,可以只调用外层流的close方法关闭其装饰的内层流。

那么如果我们想逐个关闭流,我们该怎么做?

答案是:先关闭外层流,再关闭内层流。一般情况下是:先打开的后关闭,后打开的先关闭;另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b

看懂了怎么正确的关闭流之后,那么我们就可以优化上面的代码了,只关闭外层的处理流。

finally {if( bufferedOutputStream != null ){try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}if( bufferedInputStream != null){try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}}

个人微信号:zhaoyanjun125 , 欢迎关注

weixin200.jpg

转载于:https://www.cnblogs.com/zhaoyanjun/p/6376937.html

更多相关:

  • 字节串bytes字节串也叫字节序列,是不可变的序列,存储以字节为单位的数据字节串表示方法:b"ABCD"b"x41x42"...字节串的构造函数:bytes() 创建一个空的字节串 ,同b””bytes(整数可迭代对象) 用可迭代对象创建一个字节串bytes(整数n) 生成n个值为0的字节串bytes(字符串,encoding='...

  • Unicode编码  最初的unicode编码是固定长度的,16位,也就是2两个字节代表一个字符,这样一共可以表示65536个字符。显然,这样要表示各种语言中所有的字符是远远不够的。Unicode4.0规范考虑到了这种情况,定义了一组附加字符编码,附加字符编码采用2个16位来表示,这样最多可以定义1048576个附加字符。所以4个字节...

  •   一直对编码这块晕晕乎乎,今天终于看到一篇写的很清楚也很风趣的文章,转过来mark一下。 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为”字节“。再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始...

  • 我们知道在由于大端机和小端机导致网络字节序和主机序有可能是有差异的,我们可以使用系统的ntohs,ntohl,htons和htonl这些处理函数进行转换,下面是我写的一个关于ntohs在处理小端机字节序转换的函数的简单实现. 思想大致如下: 用u_int16_t的2字节16位的整形变量来存储这个整数,首先将第一个字节和该变量进行或运算...

  • 我试图用Tkinter制作一个简单的GUI,它使用Matplotlib生成大量绘图并将它们保存到硬盘上。在附件是一个简单的代码,但是在保存了所有绘图之后,Tkinter GUI关闭,脚本停止。我想这个问题可能与plt.关闭(),因为当我删除plt.关闭(),GUI窗口不再关闭,但毫不奇怪,内存很快就会被填满,直到整个程序崩溃。在而不是...

  • 四次挥手 主动关闭连接的一方,调用close,协议层发送FIN包,在TCP报头的FIN字段设置为1,意思是我要和你断开链接,主动关闭连接的一方进入到了FIN_WATI_1状态 被动关闭的一方收到了FIN包之后,协议层回复ACK包,在他的TCP报头中将ACK设置为1,表示收到了对方的关闭连接请求,被动 的一方进入到了CLOSE_W...

  •    在TestNG运行自动化测试用例的时候,浏览器FireFox正确打开,可是在测试用例运行完成后,我调用的是webdriver.quit()关闭程序的,结果却报以下错误:    Sep 25, 2014 4:19:32 PM org.openqa.selenium.os.UnixProcess$SeleniumWatchDog d...

  • WindowsForm里一个Application.Exit();方法就可以关闭应用程序,释放掉资源。 WPF里Application类没有该方法,但是有一个Exit的事件驱动,在WPF应用程序里面关闭程序讲究很多: 在WPF应用程序的关闭是有ShutdownMode属性设置,具有3中枚举类型的值: 1)OnLastWindowClo...

  • 2019独角兽企业重金招聘Python工程师标准>>> tcpdump -XvvennSs 0 -i eth0 tcp[20:2]=0x4745 or tcp[20:2]=0x4854   0x4745 为"GET"前两个字母"GE" 0x4854 为"HTTP"前两个字母"HT" 说明: 通常情况下:一个正常的TCP连...

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...