Java之IO(二)BufferedInputStream和BufferedOutputStream
转载请注明源出处:http://www.cnblogs.com/lighten/p/6971234.html
1.前言
本文主要介绍输入输出流中的BufferedInputStream和BufferedOutStream的实现,其虽是InputStream和OutputStream的子类,但中间还隔了一个FilterInputStream和FilterOutputStream。前言中先介绍一下这两个类。这两个类都是简单的包装类。
1.1 FilterInputStream
FilterInputStream继承了父类InputStream,其没有进行什么特殊的处理,做法很简单。持有一个输入流,然后每个方法都是调用这个输入流的方法。如下图所示:
如上图所示,其覆写了InputStream的所有方法,实现过程都是直接调用持有的输入流的相应方法。这个类就是对一个输入流的简单封装罢了,但是其子类可能有其它的方法。
1.2 FilterOutputStream
FilterOutputStream和上述实现原理基本一致,也是继承自OutputStream,但是不同之处在于其中有些方法并不是直接调用持有的输出流的相应方法。
write(byte, int, int)方法比较父类,没有对输入的byte[]数组进行非空校验,其它的操作基本相同。不过不写实际上也是抛出了空指针异常。所以也没什么问题。
close()方法,先调用了flush()方法,然后关闭了这个流。这个写法有些奇怪,实际上使用的是JDK1.7 build 105之后的一个特性:try-with-resources。自动资源管理。新的语句支持包括流以及任何可关闭的资源,前提是可关闭的资源必须实现 java.lang.AutoCloseable 接口。
2.BufferedInputStream
bufferedInputStream类继承自刚刚所说的FilterInputStream类。其默认缓存大小为8192个字节,最大的缓存大小是整数类型最大值-8,即2^31-1-8。具体实现是内部有一个byte[]缓存数组。这个类比较重要的成员变量如下:
protected volatile byte buf[]; // 缓存的字节数组 private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf"); // 原子引用字段,关闭流的时候可能是异步的,使用这个的CAS机制来根据buf是否为null判断流是否关闭 protected int count; // 当前读取完的流的长度
protected int pos; // 当前已使用缓存数组中的长度,也就是下一个从缓存中读取的数组下标
protected int markpos = -1; // 调用mark方法时pos的位置
protected int marklimit; // 最大允许读取的标记长度,mark之后reset后能重复读取的最大长度
BufferedInputStream也需要一个可读的输入流,其初始化没指定缓存大小就使用默认缓存大写8192。值得一提的是其判断是否关闭都是通过是否为null来判断,即将流和缓存在关闭时设置成null。使用时判断为null就抛出流关闭IO异常。
上面是BufferedInputStream重写的read()方法。可以看出使用了锁,因此这个类是线程安全的。整个过程如下:
1.先判断读取的字节其位置pos是否超过了字节数组已缓存的大小count,如果超过了,就调用fill的方法,从流中继续读取,之后再比较一次,如果还是比当前数组记录数要大,就表示流已经读取完了,返回-1。否则返回我当前要读取的位置的值。pos移动一位。
2.fill方法如果标记位置小于0就重置pos,再将pos赋给count,调用被包装流的read(byte, int, int)方法,读取到pos位置开始,读满缓存数组。
整个调用read()方法,就是返回当前要读取的数组的那个位置,如果位置大于等于缓存内容的长度(也就是缓存内容读完了),就去重新填满缓存,pos和count重新计算。否则就没读完缓存内容,就返回缓存中这个位置的值。这个过程又涉及到标记位置,所以如果有标记位的话,就不能简单的重置缓存和计数了。这也就是中间那大段的逻辑了。
close()方法也就是前面提到的,将bufUpdater中的buffer置为null,然后关闭流。
skip方法过程就是:1.判读流是否还开启;2.计算当前未读取的缓存;3.如果当前没有可读缓存,且没有标记,就调用流的skip跳过流中的字节,返回跳过数;4.如果有标记,就通过fill()方法,将标记的那段重置一下,再计算一次可用缓存,如果还是没有就返回跳过0。5.如果缓存中有可用,就正常跳过,pos位置加上跳过的值。
也就是说skip方法在缓存有未读取的时候,最多只会跳过目前缓存中的值,没有缓存的时候,也没标记则会跳过流中的个数。若没缓冲有标记的时候,就会重置缓存,读取流,如果此时可用的长度还为0则没有跳过项,否则就最多跳过目前缓存中的值。
还有一个read(byte[], int, int)方法就不介绍了,过程也比较好懂。
小结一下,BufferInputStream最重要的字段就是pos,count。负责调控整个调用过程。pos是当前将要读取位置,count是已缓存的流的长度。读取和跳过都是优先缓存的,缓存中没有,才会到流中去操作。fill()方法就是用来将流中的数据填充到缓存中,但这个方法有一个前提,那就是必须是被同步方法块调用,并且缓存必须是被读完了,即之后就会pos > count。如果不满足,则不能调用这个方法。理解这一点就比较好理解这个运算过程了,不然看别人代码就比较困难,这个就是此类的设计思路了。
3.BufferedOutputStream
BufferedOutputStream比较简单,里面就两个成员变量。这个类的主要作用就是避免了反复一个个write(byte)。
buf[]就是缓存的数据字节数组,count就是缓存中内容长度了。默认缓冲大小也是8192。
实现也很简单,如果缓存的长度操作数组长度,就整个缓存输入包装的输出流。否者就放入缓存数据中,count位置+1。
输入一个数组,会先判断是否超过缓存长度,超过了,就先刷目前的缓存,再直接调用包装输出流的write(byte[], int, int)方法,不走缓存。如果比缓存大小小,但大于可用长度,还是先刷入目前的缓存。之后就是将数组拷贝到缓存可用的后面。
flush()方法就是先刷新缓存,在调用包装的输出流的flush()方法。
4.后记
缓存输入输出流都是对一个已有的流进行包装,最终都是调用已有的流相关方法。这两者都是缓存优先的,所以使用的时候要注意,比如输入流的skip方法使用就要注意了。输入流缓存,避免了使用in.read()一个个读取,关键是使得mark()方法可用(流一般都是读取完了就没了,缓存就意味着可以重置),输出流也是避免了一个个byte输出。最后两者都是线程安全的。
Java之IO(二)BufferedInputStream和BufferedOutputStream的更多相关文章
- Java IO流 BufferedInputStream、BufferedOutputStream的基本使用
BufferedInputStream.BufferedOutputStream的基本使用 BufferedInputStream是FilterInputStream流的子类,FilterInputS ...
- Java基础---IO(二)--File类、Properties类、打印流、序列流(合并流)
第一讲 File类 一.概述 1.File类:文件和目录路径名的抽象表现形式 2.特点: 1)用来将文件或文件夹封装成对象 2)方便于对文件与文件夹的属性信息进行操作 3)File类的实例是不 ...
- Java之IO(零)总结
转载请注明原出处:http://www.cnblogs.com/lighten/p/7274378.html 1.前言 本章是对之前所讲述的整个Java的IO包的一个总结,抽出个人认为比较重要的知识点 ...
- Java基础-IO流对象之字节缓冲流(BufferedOutputStream与BufferedInputStream)
Java基础-IO流对象之字节缓冲流(BufferedOutputStream与BufferedInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在我们学习字 ...
- Java IO(十) BufferedInputStream 和 BufferedOutputStream
Java IO(十)BufferedInputStream 和 BufferedOutputStream 一.BufferedInputStream 和 BufferedOutputStream (一 ...
- Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream
Java IO流学习总结三:缓冲流-BufferedInputStream.BufferedOutputStream 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/ ...
- java 输入输出IO流 字节流| 字符流 的缓冲流:BufferedInputStream;BufferedOutputStream;BufferedReader(Reader in);BufferedWriter(Writer out)
什么是缓冲流: 缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率. 图解: 1.字节缓冲流BufferedInputStr ...
- Java IO(二)
字节流 字符流: FileReader FileWriter BufferedReader BufferedWriter 字节流: FileInputStream FileOutputStream B ...
- java io系列13之 BufferedOutputStream(缓冲输出流)的认知、源码和示例
本章内容包括3个部分:BufferedOutputStream介绍,BufferedOutputStream源码,以及BufferedOutputStream使用示例. 转载请注明出处:http:// ...
随机推荐
- VS2010/MFC编程(对话框:模态对话框及其弹出过程)
讲讲什么是模态对话框和非模态对话框,以及模态对话框怎样弹出. 一.模态对话框和非模态对话框 Windows对话框分为两类:模态对话框和非模态对话框. 模态对话框是这样的对话框,当它弹出后,本应用程序其 ...
- python文件对比
#-*- encoding:utf-8 -*- class loadDatas(object): def __init__(self): self.path='./data' def load_com ...
- NATAPP打穿内网
前一篇博文写了ngrok作为内网穿透工具现在开始开始学习另外一种内 网穿透natapp(他也是基于ngrok的一种高速内网穿透服务)多的介绍就 不说了,开始进入正题. 第一步:先登录natapp官网( ...
- (最小生成树)Agri-Net -- POJ -- 1258
链接: http://poj.org/problem?id=1258 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82831#probl ...
- Hdu1969 Pie 2017-01-17 13:12 33人阅读 评论(0) 收藏
Pie Time Limit : 5000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submissio ...
- PAT甲 1011. World Cup Betting (20) 2016-09-09 23:06 18人阅读 评论(0) 收藏
1011. World Cup Betting (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Wit ...
- 发展科技到底有什么用,转NASA专家给一位修女的一封信
问题补充:我们难道不应该把这些资金用于更深入的医疗保障和减少贫穷吗? 我们为何要仰望星空,花大量的金钱和精力探索那不可预知的宇宙呢?NASA科学家写给非洲修女的一封信回答得特别好,也特别震撼人心.—— ...
- Python学习-32.Python中os模块的一些方法
首先肯定是要引入os模块了. import os getcwd方法: print(os.getcwd()) 上面的语句将会输出当前的工作目录,相当于C#中的Environment.CurrentDir ...
- DBCC--OPENTRAN
返回最早开始的但仍在运行的事务 数据库 'DB1' 的事务信息. 最早的活动事务: SPID (服务器进程 ID): 60 UID (用户 ID): -1 名称 : user_tra ...
- 二叉树(Binary Tree)相关算法的实现
写在前面: 二叉树是比较简单的一种数据结构,理解并熟练掌握其相关算法对于复杂数据结构的学习大有裨益 一.二叉树的创建 [不喜欢理论的点我跳过>>] 所谓的创建二叉树,其实就是让计算机去存储 ...