FilterInputStream与装饰模式(2)--BufferedInputStream源码
来源:互联网 发布:java的排序函数 编辑:程序博客网 时间:2024/06/01 10:50
BufferedInputStream :存在缓冲器的输入流,并提供mark和reset方法,在 BufferedInputStream构造器初始化时,会创建一个缓冲数组。mark方法标记输入流的当前位置点,reset方法会从最近一次的mark点重新读取流数据。
BufferedInputStream 源码:
public class BufferedInputStream extends FilterInputStream { //存储数据的缓冲数组;必要是可以被另外一个不同大小的数组替换;volatile表明是多线程即时可见 protected volatile byte buf[]; //缓冲数组的原子更新器,针对volatile修饰的buf数组 private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class, byte[].class, "buf"); //缓冲数组中有效的字节数,buf[0]和buf[count-1]存储了缓冲的数据流 protected int count; //缓冲数组的当前读取位置,值在0和count之间 protected int pos; //最后一次mark方法调用后,存储的标记位置,值在-1和pos之间;当markpos!=-1,则从buf[markpos]至buf[pos-1]之间的值均在缓冲数组中保留,如果markpos=0,在pos读到缓冲数组末尾时,如果buffer的长度超过了marklimit则此markpos标记会被抛弃 protected int markpos = -1; //保留标记位置的最大区间,buffer的长度大于marklimit,标记位置不再保留。 protected int marklimit; //检验底层数据流是否为null,如果不为null,则返回此流 private InputStream getInIfOpen() throws IOException { InputStream input = in; if (input == null) throw new IOException("Stream closed"); return input; } //校验缓冲数组是否为null,不为null,则返回此数组 private byte[] getBufIfOpen() throws IOException { byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); return buffer; } //构造函数,defaultBufferSize默认大小为8192 public BufferedInputStream(InputStream in) { this(in, defaultBufferSize); } //构造函数,同时创建一个大小为size的缓冲数组 public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } /*用数据来填充缓存数组,填充时规则: 1 如果没有标记位置(mark<0),则可以将缓冲数组中的数据都舍弃(throw away),将pos=0,以备继续读取底层流数据,覆盖原数据(全部覆盖还是只覆盖数组前面一部分,取决于还有多少流数据要被读取); 2 如果有标记位置(markpos>=0),且pos<buffer.length时,表明buffer中有空间,则可以继续读取流数据 3 如果有标记位置(markpos>=0),且pos>=buffer.length时,表明已经读到缓冲数组的末尾。 1) 如果markpos>0,也就是说此时标记位置markpos到pos之间的数据需要保存(以便reset),而缓冲数组中markpos之前的数据可以舍弃。将缓冲数组中需要保留的数据复制到此数组前面sz=pos-markpos的空间中,pos为sz,markpos=0为起始位置。然后可以从底层数据流中继续缓冲进buffer中大小为length-pos的数据。count为实际读取的字节大小+pos. 2) 如果markpos=0,且buffer的长度大于marklimit,则标记位置不再保留,pos=0,此时将缓冲数组的数据舍弃,继续读取底层流数据(即走规则1),当调用reset方法时,此时会报异常(Resettin to invalid mark) 3)如果markpos=0,且buffer.length<=marklimit,则对buffer扩容,buffer的大小扩展为pos*2(当pos*2<marklimit时;如果pos*2>marklimit,则buffer的扩容后的大小为marklimit),并将原buffer中保存的数据拷贝到新buffer中,继续读取底层流数据。 注:通过修改defaultBufferSize的大小,本地可以重现以上各种规则下的fill操作 */ private void fill() throws IOException { byte[] buffer = getBufIfOpen(); if (markpos < 0) pos = 0; /* no mark: throw away the buffer 没有标记位置时,将pos定位到缓冲数组的起始位置 */ else if (pos >= buffer.length) /* no room left in buffer */ if (markpos > 0) { /* can throw away early part of the buffer */ int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; /* buffer got too big, invalidate mark */ pos = 0; /* drop buffer contents */ } else { /* grow buffer */ int nsz = pos * 2; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { // Can't replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf == null; throw new IOException("Stream closed"); } buffer = nbuf; } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; } //返回当前位置+1的byte值(int表示)。 //byte[i] & 0xff:此操作是将byte数据转成int型;使用了这个操作,最终的整形数据只有低8位有数据,其他位数都为0。 (0xff二进制一共32位,后8位是1,其他位为0) public synchronized int read() throws IOException { if (pos >= count) {//判断当前位置是否>=count值,如果是,则进行fill操作,填充缓冲数组 fill(); if (pos >= count) return -1; } //如果pos小于count值,则返回数组当前索引的值 return getBufIfOpen()[pos++] & 0xff; }}
补充其他方法如下:
/*read1方法,此方法为私有方法: * 判断缓冲数组是否可读, * 1、如果可读,从缓冲数组中读取cnt长度,cnt是可读长度与欲读长度的较小者。 * 2、如果avail<=0,即缓冲数组中没有可读字节,判断len是否大于缓冲数组长度且无mark标记, * 如果是,则直接从底层流读取数据,并返回 * 如果不是,则fill缓冲数组,继续判断是否可读,如果可读走流程1,不可读则已到达流末尾,直接返回-1. */ private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { /* If the requested length is at least as large as the buffer, and if there is no mark/reset activity, do not bother to copy the bytes into the local buffer. In this way buffered streams will cascade harmlessly. */ if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); } fill(); avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; System.arraycopy(getBufIfOpen(), pos, b, off, cnt); pos += cnt; return cnt; } /*此方法尽可能的读取数据,直到达到欲读长度len,或流的末尾,才停止。通过调用read1方法实现。 * 根据条件判断是否达到欲读长度len或流的末尾来一次或多次调用read1方法 */ public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); // Check for closed stream if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } } /* 略过多少字节 * 返回实际略过的字节数 */ public synchronized long skip(long n) throws IOException { getBufIfOpen(); // Check for closed stream if (n <= 0) { return 0; } long avail = count - pos; if (avail <= 0) { // If no mark position set then don't keep in buffer if (markpos <0) return getInIfOpen().skip(n); // Fill in buffer to save bytes for reset fill(); avail = count - pos; if (avail <= 0) return 0; } long skipped = (avail < n) ? avail : n; pos += skipped; return skipped; } //返回可读字节数量 public synchronized int available() throws IOException { int n = count - pos; int avail = getInIfOpen().available(); return n > (Integer.MAX_VALUE - avail) ? Integer.MAX_VALUE : n + avail; } // mark方法,标记当前位置 public synchronized void mark(int readlimit) { marklimit = readlimit; markpos = pos; } //reset方法:重置当前位置为markpos public synchronized void reset() throws IOException { getBufIfOpen(); // Cause exception if closed if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } //是否支持mark和reset方法 public boolean markSupported() { return true; } //关闭流,并释放缓冲数组中的数据 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() } }
阅读全文
0 0
- FilterInputStream与装饰模式(2)--BufferedInputStream源码
- FilterInputStream与装饰模式(1)
- Java8 I/O源码-FilterInputStream、FilterOutputStream与装饰器模式
- java io -- FilterInputStream 与 装饰者模式
- FilterInputStream之装饰模式(例)
- Java8 I/O源码-BufferedInputStream与BufferedOutputStream
- FilterInputStream与FilterOutputStream
- 2-Java IO与装饰模式
- Android源码装饰模式---ContextWrapper
- cppunit与装饰模式
- 设计模式之装饰模式与IO--JDK源码设计模式应用
- BufferedInputStream源码学习
- BufferedInputStream 源码分析
- BufferedInputStream源码分析
- BufferedInputStream源码分析
- BufferedInputStream 源码学习笔记
- BufferedInputStream 源码学习笔记
- 【java与模式】装饰模式
- 设置静态IP
- Android Project和app中两个build.gradle配置的区别
- Debugger Engine API
- 小试牛刀(一)
- OutputStreamWriter分析
- FilterInputStream与装饰模式(2)--BufferedInputStream源码
- Android 通过seekBar设置图片透明度
- Code reviewing..............
- C++ memory ordering
- scrapy
- JAVA的String类 (转)
- Debugger Engine API
- 17_7_19:实现一个栈要求实现入栈、出栈、获得最小值的时间复杂度为O(1)
- HDU 3666 THE MATRIX PROBLEM (建图+差分约束+剪枝优化)