Java字节流:BufferedInputStream BufferedOutputStream
-----------------------------------------------------------------------------------
BufferedInputStream
类声明:public class BufferedInputStream extends FilterInputStream
位于java.io包下
官方对其说明:
A BufferedInputStream adds functionality to another input stream-namely, the ability to buffer the input and to support the mark and reset methods. When the BufferedInputStream is created, an internal buffer array is created. As bytes from the stream are read or skipped, the internal buffer is refilled as necessary from the contained input stream, many bytes at a time. The mark operation remembers a point in the input stream and the reset operation causes all the bytes read since the most recent mark operation to be reread before new bytes are taken from the contained input stream.
(简单翻译:BufferedInputStream为另一个输入流添加一些功能,即缓冲输入以及支持mark和reset方法的能力,在创建BufferedInputStream时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所字节。)
主要字段:
protected byte[] buf;//存储数据的内部缓冲区数组
protected int count;//缓冲区中有效字节的个数
protected int marklimit;//调用mark方法后,在后续调用reset方法失败之前允许的最大提前读取量
protected int markpos;//最后一次调用mark方法时pos字段的值
protected int pos;//缓冲区中的当前位置
构造方法:
BufferedInputStream(InputStream in)
创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用。
BufferedInputStream(InputStream in,int size)
创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流in,以便将来使用。
主要方法:
- int available(): 返回缓存字节输入流中可读取的字节数
- void close(): 关闭此缓存字节输入流并释放与该流有关的系统资源.
- void mark(int readlimit): 在流中标记位置
- boolean markSupported(): 测试该输入流是否支持mark和reset方法
- int read(): 从缓冲输入流中读取一个字节数据
- int read(byte[] b,int off,int len): 从缓冲输入流中将最多len个字节的数据读入到字节数组b中
- long skip(long n): 从缓冲输入流中跳过并丢弃n个字节的数据
首先我们要明白BufferedInputStream的思想,它的作用就是为其它输入流提供缓冲功能。创建BufferedInputStream时我们会通过它的构造函数指定某个输入流为参数,BufferedInputStream会将该输入流数据分批读取,每次读取一部分到缓冲区中,操作完缓冲区中的数据后,再次从输入流中读取下一部分的数据。
BufferedInputStream 缓冲字节输入流,它作为FilterInputStream的一个子类,为传入的底层字节输入流提供缓冲功能,通过底层字节输入流(in)读取字节到自己的buffer中(内置缓存字节数组),然后程序调用BufferedInputStream的read方法将buffer中的字节读取到程序中,当buffer中的字节被读取完之后,BufferedInputStream会从in中读取下一批数据块到buffer中,直到in中的数据被读取完毕,这样做的好处是提高读取的效率和减少打开存储介质的链接次数。
下面就通过构造函数来创建一个BufferedInputStream实例
重点查看read()和fill()方法
bis.txt文件的内容为:qwertyuiopasdfghjklzxcvbnm
//指定输入流为FileInputStream、 缓冲区大小为10
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"),10);
此时BufferedInputStream在内存中的情况如下图:
此时只是创建出一个BufferedInputStream的实例bis, 其缓冲区中并没有任何的数据,下面可以通过执行read()方法将输入流in中的输入读取到缓冲区中。
先来看看read()方法的源代码:
1 public synchronized int read() throws IOException { 2 if (pos >= count) { 3 fill();//调用fill()方法从输入流in中将数据读取到缓冲区中 4 if (pos >= count) 5 return -1; 6 } 7 return getBufIfOpen()[pos++] & 0xff; 8 }
当第一次执行bis.read()方法时,因为属性pos=0、count=0,所以一定会去执行fill()方法,下面我们就先转到fill()方法去看看。
fill()方法的源代码如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 if (markpos < 0) 4 pos = 0; /* no mark: throw away the buffer */ 5 else if (pos >= buffer.length) /* no room left in buffer */ 6 if (markpos > 0) { /* can throw away early part of the buffer */ 7 int sz = pos - markpos; 8 System.arraycopy(buffer, markpos, buffer, 0, sz); 9 pos = sz; 10 markpos = 0; 11 } else if (buffer.length >= marklimit) { 12 markpos = -1; /* buffer got too big, invalidate mark */ 13 pos = 0; /* drop buffer contents */ 14 } else { /* grow buffer */ 15 int nsz = pos * 2; 16 if (nsz > marklimit) 17 nsz = marklimit; 18 byte nbuf[] = new byte[nsz]; 19 System.arraycopy(buffer, 0, nbuf, 0, pos); 20 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 21 // Can't replace buf if there was an async close. 22 // Note: This would need to be changed if fill() 23 // is ever made accessible to multiple threads. 24 // But for now, the only way CAS can fail is via close. 25 // assert buf == null; 26 throw new IOException("Stream closed"); 27 } 28 buffer = nbuf; 29 } 30 count = pos; 31 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 32 if (n > 0) 33 count = n + pos; 34 }
当此时从read()方法中调用fill()方法时,因为属性markpos=-1,所以可以把fill()方法不会执行的else部分先去掉:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 if (markpos < 0) 4 pos = 0; /* no mark: throw away the buffer */ 5 count = pos; 6 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 7 if (n > 0) 8 count = n + pos; 9 }
看上面简化版的fill()方法就很清楚了,会通过文件输入流的read(byte[] b, int off, int len)方法将字节输入读取到bis的缓冲区中,执行完fill()方法后,bis在内存中的情况如下:
可以看出实例bis中的各个属性值的情况。由此我们可以知道fill()方法从输入流中读取字节数据到bis实例的缓冲区中。
第一次调用read()方法时,内部会去调用fill()方法(fill方法的执行效果如上图所示),read()方法执行完后返回 113,bis在内存中的情况:
属性count=10、pos=1
第二次、第三次、第四次、第五次、第六次、第七次、第八次、第九次调用read()方法时,因为:pos的值都小于count,所以read()方法只会执行下面的代码:
1 return getBufIfOpen()[pos++] & 0xff;
执行完9次read()方法后,此时bis在内存中的情况如下:属性pos=10,这样下一次去调用read()方法时又会去执行fill()方法了。
当执行第10次read()方法时,因为pos的值为10,所以又会去调用fill()方法,此时因为markpost的值还是为-1,所以fill()方法简化后如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 if (markpos < 0) 4 pos = 0; /* no mark: throw away the buffer */ 5 count = pos; 6 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 7 if (n > 0) 8 count = n + pos; 9 }
执行完fill()方法后,bis在内存中的情况如下:
从上图可以看出,pos的值被修改为0,buf数组中存储的值为in输入流中下10个字节.由此我们就可以知道:BufferedInputStream类在其内部提供了一个缓冲区来存储从输入流中读取的数据,每次读取一批数据到缓冲区中供程序使用,当缓冲区中的数据使用完了以后,再次从输入流中读取下一批,直到in输入流的末尾。
上面我们分析了BufferedInputStream中的fill()和read()方法,但是在属性markpos>=0的情况下还没有分析,要修改markpos的值 需要调用mark(int readlimit)方法:
1 public synchronized void mark(int readlimit) { 2 marklimit = readlimit; 3 markpos = pos; 4 }
根据实例bis中的不同属性值,fill方法会有如下5个执行流程:
流程1:当if (pos >= count)并且markpos的值为-1时:
程序执行流程如下:
(1)执行read()方法,转到到fill()方法
(2)fill()方法中,执行if(markpos < 0) 这个分支
简化后的代码如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 if (markpos < 0) 4 pos = 0; /* no mark: throw away the buffer */ 5 count = pos; 6 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 7 if (n > 0) 8 count = n + pos; 9 }
这种流程发生的情况是 ---->输入流中有很多的数据,我们每次从中读取一部分数据到缓冲区buffer中进行操作。每次当我们读取完buffer中的数据之后,并且此时输入流没有被标记;那么,就接着从输入流中读取下一部分的数据到buffer中。
其中,判断是否读完buffer中的数据,是通过 if (pos >= count) 来判断的;
判断输入流有没有被标记,是通过 if (markpos < 0) 来判断的。
理解这个思想之后,我们再对这种情况下的fill()的代码进行分析,就特别容易理解了。
(1) if (markpos < 0) 它的作用是判断“输入流是否被标记”。若被标记,则markpos大于/等于0;否则markpos等于-1。
(2) 在这种情况下:通过getInIfOpen()获取输入流,然后接着从输入流中读取buffer.length个字节到buffer中。
(3) count = n + pos; 这是根据从输入流中读取的实际数据的多少,来更新buffer中数据的实际大小。
流程2:当if (pos >= count)、markpos>0、if (pos >= buffer.length)时:
程序执行流程如下:
(1) read() 函数中调用 fill()
(2) fill() 中的 else if (pos >= buffer.length) ...
(3) fill() 中的 if (markpos > 0) ...
简化后的代码如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 4 if (pos >= buffer.length) /* no room left in buffer */ 5 if (markpos > 0) { /* can throw away early part of the buffer */ 6 int sz = pos - markpos; 7 System.arraycopy(buffer, markpos, buffer, 0, sz); 8 pos = sz; 9 markpos = 0; 10 } 11 count = pos; 12 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 13 if (n > 0) 14 count = n + pos; 15 }
这种流程发生的情况是 ----> 输入流中有很多的数据,我们每次从中读取一部分数据到buffer中进行操作。当我们读取完buffer中的数据之后,并且此时输入流存在标记时;那么,就发生流程2。此时,我们要保留“被标记位置”到“buffer末尾”的数据,然后再从输入流中读取下一部分的数据到buffer中。
其中,判断是否读完buffer中的数据,是通过 if (pos >= count) 来判断的;
判断输入流有没有被标记,是通过 if (markpos < 0) 来判断的。
判断buffer中没有多余的空间,是通过 if (pos >= buffer.length) 来判断的。
理解这个思想之后,我们再对这种情况下的fill()代码进行分析,就特别容易理解了。
(1) int sz = pos - markpos; 作用是“获取‘被标记位置’到‘buffer末尾’”的数据长度。
(2) System.arraycopy(buffer, markpos, buffer, 0, sz); 作用是“将buffer中从markpos开始的数据”拷贝到buffer中(从位置0开始填充,填充长度是sz)。接着,将sz赋值给pos,即pos就是“被标记位置”到“buffer末尾”的数据长度。
(3) int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 从输入流中读取出“buffer.length - pos”的数据,然后填充到buffer中。
(4) 通过第(02)和(03)步组合起来的buffer,就是包含了“原始buffer被标记位置到buffer末尾”的数据,也包含了“从输入流中新读取的数据”。
注意:执行过流程2之后,markpos的值由“大于0”变成了“等于0”!
流程3:当if (pos >= count)、if(pos >= buffer.length)、if(buffer.length >= marklimit)时:
程序执行流程如下:
(1) read() 函数中调用 fill()
(2) fill() 中的 else if (pos >= buffer.length)
(3) fill() 中的 else if (buffer.length >= marklimit)
简化后的代码如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 if (pos >= buffer.length) /* no room left in buffer */ 4 if (buffer.length >= marklimit) { 5 markpos = -1; /* buffer got too big, invalidate mark */ 6 pos = 0; /* drop buffer contents */ 7 } 8 count = pos; 9 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 10 if (n > 0) 11 count = n + pos; 12 }
说明:这种情况的处理非常简单。首先,就是“取消标记”,即 markpos = -1;然后,设置初始化位置为0,即pos=0;最后,再从输入流中读取下一部分数据到buffer中。
流程4:当if (pos >= count)、if(pos >= buffer.length)、markpos=0时:
程序执行流程如下:
(1) read() 函数中调用 fill()
(2) fill() 中的 else if (pos >= buffer.length)
(3) fill() 中的 else { int nsz = pos * 2}
简化后的代码如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 4 if (pos >= buffer.length){ /* no room left in buffer */ 5 /* grow buffer */ 6 int nsz = pos * 2; 7 if (nsz > marklimit) 8 nsz = marklimit; 9 byte nbuf[] = new byte[nsz]; 10 System.arraycopy(buffer, 0, nbuf, 0, pos); 11 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 12 // Can't replace buf if there was an async close. 13 // Note: This would need to be changed if fill() 14 // is ever made accessible to multiple threads. 15 // But for now, the only way CAS can fail is via close. 16 // assert buf == null; 17 throw new IOException("Stream closed"); 18 } 19 buffer = nbuf; 20 } 21 count = pos; 22 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 23 if (n > 0) 24 count = n + pos; 25 }
说明:
这种情况的处理非常简单。
(1) 新建一个字节数组nbuf。nbuf的大小是“pos*2”和“marklimit”中较小的那个数。
(2) 接着,将buffer中的数据拷贝到新数组nbuf中。通过System.arraycopy(buffer, 0, nbuf, 0, pos)
(3) 最后,从输入流读取部分新数据到buffer中。通过getInIfOpen().read(buffer, pos, buffer.length - pos);
注意:在这里,我们思考一个问题,“为什么需要marklimit,它的存在到底有什么意义?”我们结合“情况2”、“情况3”、“情况4”的情况来分析。
假设,marklimit是无限大的,而且我们设置了markpos。当我们从输入流中每读完一部分数据并读取下一部分数据时,都需要保存markpos所标记的数据;这就意味着,我们需要不断执行情况4中的操作,要将buffer的容量扩大……随着读取次数的增多,buffer会越来越大;这会导致我们占据的内存越来越大。所以,我们需要给出一个marklimit;当buffer>=marklimit时,就不再保存markpos的值了。
流程5:除了上面4种情况之外的流程:
执行流程如下:
(1)read()函数中调用fill()方法
(2)fill()中的count = pos
简化后的代码如下:
1 private void fill() throws IOException { 2 byte[] buffer = getBufIfOpen(); 3 4 count = pos; 5 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 6 if (n > 0) 7 count = n + pos; 8 }
说明:这种情况的处理很简单,就是直接从输入流读取部分数据到buffer中.
BufferedInputStream类中的其它方法都很简单,直接查看源代码就好了。
BufferedInputStream源代码如下:
1 package java.io; 2 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; 3 4 /** 5 * A <code>BufferedInputStream</code> adds 6 * functionality to another input stream-namely, 7 * the ability to buffer the input and to 8 * support the <code>mark</code> and <code>reset</code> 9 * methods. When the <code>BufferedInputStream</code> 10 * is created, an internal buffer array is 11 * created. As bytes from the stream are read 12 * or skipped, the internal buffer is refilled 13 * as necessary from the contained input stream, 14 * many bytes at a time. The <code>mark</code> 15 * operation remembers a point in the input 16 * stream and the <code>reset</code> operation 17 * causes all the bytes read since the most 18 * recent <code>mark</code> operation to be 19 * reread before new bytes are taken from 20 * the contained input stream. 21 * 22 * @author Arthur van Hoff 23 * @since JDK1.0 24 */ 25 public 26 class BufferedInputStream extends FilterInputStream { 27 28 private static int defaultBufferSize = 8192; 29 30 /** 31 * The internal buffer array where the data is stored. When necessary, 32 * it may be replaced by another array of 33 * a different size. 34 */ 35 protected volatile byte buf[]; 36 37 /** 38 * Atomic updater to provide compareAndSet for buf. This is 39 * necessary because closes can be asynchronous. We use nullness 40 * of buf[] as primary indicator that this stream is closed. (The 41 * "in" field is also nulled out on close.) 42 */ 43 private static final 44 AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = 45 AtomicReferenceFieldUpdater.newUpdater 46 (BufferedInputStream.class, byte[].class, "buf"); 47 48 /** 49 * The index one greater than the index of the last valid byte in 50 * the buffer. 51 * This value is always 52 * in the range <code>0</code> through <code>buf.length</code>; 53 * elements <code>buf[0]</code> through <code>buf[count-1] 54 * </code>contain buffered input data obtained 55 * from the underlying input stream. 56 */ 57 protected int count; 58 59 /** 60 * The current position in the buffer. This is the index of the next 61 * character to be read from the <code>buf</code> array. 62 * <p> 63 * This value is always in the range <code>0</code> 64 * through <code>count</code>. If it is less 65 * than <code>count</code>, then <code>buf[pos]</code> 66 * is the next byte to be supplied as input; 67 * if it is equal to <code>count</code>, then 68 * the next <code>read</code> or <code>skip</code> 69 * operation will require more bytes to be 70 * read from the contained input stream. 71 * 72 * @see java.io.BufferedInputStream#buf 73 */ 74 protected int pos; 75 76 /** 77 * The value of the <code>pos</code> field at the time the last 78 * <code>mark</code> method was called. 79 * <p> 80 * This value is always 81 * in the range <code>-1</code> through <code>pos</code>. 82 * If there is no marked position in the input 83 * stream, this field is <code>-1</code>. If 84 * there is a marked position in the input 85 * stream, then <code>buf[markpos]</code> 86 * is the first byte to be supplied as input 87 * after a <code>reset</code> operation. If 88 * <code>markpos</code> is not <code>-1</code>, 89 * then all bytes from positions <code>buf[markpos]</code> 90 * through <code>buf[pos-1]</code> must remain 91 * in the buffer array (though they may be 92 * moved to another place in the buffer array, 93 * with suitable adjustments to the values 94 * of <code>count</code>, <code>pos</code>, 95 * and <code>markpos</code>); they may not 96 * be discarded unless and until the difference 97 * between <code>pos</code> and <code>markpos</code> 98 * exceeds <code>marklimit</code>. 99 * 100 * @see java.io.BufferedInputStream#mark(int) 101 * @see java.io.BufferedInputStream#pos 102 */ 103 protected int markpos = -1; 104 105 /** 106 * The maximum read ahead allowed after a call to the 107 * <code>mark</code> method before subsequent calls to the 108 * <code>reset</code> method fail. 109 * Whenever the difference between <code>pos</code> 110 * and <code>markpos</code> exceeds <code>marklimit</code>, 111 * then the mark may be dropped by setting 112 * <code>markpos</code> to <code>-1</code>. 113 * 114 * @see java.io.BufferedInputStream#mark(int) 115 * @see java.io.BufferedInputStream#reset() 116 */ 117 protected int marklimit; 118 119 /** 120 * Check to make sure that underlying input stream has not been 121 * nulled out due to close; if not return it; 122 */ 123 private InputStream getInIfOpen() throws IOException { 124 InputStream input = in; 125 if (input == null) 126 throw new IOException("Stream closed"); 127 return input; 128 } 129 130 /** 131 * Check to make sure that buffer has not been nulled out due to 132 * close; if not return it; 133 */ 134 private byte[] getBufIfOpen() throws IOException { 135 byte[] buffer = buf; 136 if (buffer == null) 137 throw new IOException("Stream closed"); 138 return buffer; 139 } 140 141 /** 142 * Creates a <code>BufferedInputStream</code> 143 * and saves its argument, the input stream 144 * <code>in</code>, for later use. An internal 145 * buffer array is created and stored in <code>buf</code>. 146 * 147 * @param in the underlying input stream. 148 */ 149 public BufferedInputStream(InputStream in) { 150 this(in, defaultBufferSize); 151 } 152 153 /** 154 * Creates a <code>BufferedInputStream</code> 155 * with the specified buffer size, 156 * and saves its argument, the input stream 157 * <code>in</code>, for later use. An internal 158 * buffer array of length <code>size</code> 159 * is created and stored in <code>buf</code>. 160 * 161 * @param in the underlying input stream. 162 * @param size the buffer size. 163 * @exception IllegalArgumentException if size <= 0. 164 */ 165 public BufferedInputStream(InputStream in, int size) { 166 super(in); 167 if (size <= 0) { 168 throw new IllegalArgumentException("Buffer size <= 0"); 169 } 170 buf = new byte[size]; 171 } 172 173 /** 174 * Fills the buffer with more data, taking into account 175 * shuffling and other tricks for dealing with marks. 176 * Assumes that it is being called by a synchronized method. 177 * This method also assumes that all data has already been read in, 178 * hence pos > count. 179 */ 180 private void fill() throws IOException { 181 byte[] buffer = getBufIfOpen(); 182 if (markpos < 0) 183 pos = 0; /* no mark: throw away the buffer */ 184 else if (pos >= buffer.length) /* no room left in buffer */ 185 if (markpos > 0) { /* can throw away early part of the buffer */ 186 int sz = pos - markpos; 187 System.arraycopy(buffer, markpos, buffer, 0, sz); 188 pos = sz; 189 markpos = 0; 190 } else if (buffer.length >= marklimit) { 191 markpos = -1; /* buffer got too big, invalidate mark */ 192 pos = 0; /* drop buffer contents */ 193 } else { /* grow buffer */ 194 int nsz = pos * 2; 195 if (nsz > marklimit) 196 nsz = marklimit; 197 byte nbuf[] = new byte[nsz]; 198 System.arraycopy(buffer, 0, nbuf, 0, pos); 199 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 200 // Can't replace buf if there was an async close. 201 // Note: This would need to be changed if fill() 202 // is ever made accessible to multiple threads. 203 // But for now, the only way CAS can fail is via close. 204 // assert buf == null; 205 throw new IOException("Stream closed"); 206 } 207 buffer = nbuf; 208 } 209 count = pos; 210 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 211 if (n > 0) 212 count = n + pos; 213 } 214 215 /** 216 * See 217 * the general contract of the <code>read</code> 218 * method of <code>InputStream</code>. 219 * 220 * @return the next byte of data, or <code>-1</code> if the end of the 221 * stream is reached. 222 * @exception IOException if this input stream has been closed by 223 * invoking its {@link #close()} method, 224 * or an I/O error occurs. 225 * @see java.io.FilterInputStream#in 226 */ 227 public synchronized int read() throws IOException { 228 if (pos >= count) { 229 fill(); 230 if (pos >= count) 231 return -1; 232 } 233 return getBufIfOpen()[pos++] & 0xff; 234 } 235 236 /** 237 * Read characters into a portion of an array, reading from the underlying 238 * stream at most once if necessary. 239 */ 240 private int read1(byte[] b, int off, int len) throws IOException { 241 int avail = count - pos; 242 if (avail <= 0) { 243 /* If the requested length is at least as large as the buffer, and 244 if there is no mark/reset activity, do not bother to copy the 245 bytes into the local buffer. In this way buffered streams will 246 cascade harmlessly. */ 247 if (len >= getBufIfOpen().length && markpos < 0) { 248 return getInIfOpen().read(b, off, len); 249 } 250 fill(); 251 avail = count - pos; 252 if (avail <= 0) return -1; 253 } 254 int cnt = (avail < len) ? avail : len; 255 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); 256 pos += cnt; 257 return cnt; 258 } 259 260 /** 261 * Reads bytes from this byte-input stream into the specified byte array, 262 * starting at the given offset. 263 * 264 * <p> This method implements the general contract of the corresponding 265 * <code>{@link InputStream#read(byte[], int, int) read}</code> method of 266 * the <code>{@link InputStream}</code> class. As an additional 267 * convenience, it attempts to read as many bytes as possible by repeatedly 268 * invoking the <code>read</code> method of the underlying stream. This 269 * iterated <code>read</code> continues until one of the following 270 * conditions becomes true: <ul> 271 * 272 * <li> The specified number of bytes have been read, 273 * 274 * <li> The <code>read</code> method of the underlying stream returns 275 * <code>-1</code>, indicating end-of-file, or 276 * 277 * <li> The <code>available</code> method of the underlying stream 278 * returns zero, indicating that further input requests would block. 279 * 280 * </ul> If the first <code>read</code> on the underlying stream returns 281 * <code>-1</code> to indicate end-of-file then this method returns 282 * <code>-1</code>. Otherwise this method returns the number of bytes 283 * actually read. 284 * 285 * <p> Subclasses of this class are encouraged, but not required, to 286 * attempt to read as many bytes as possible in the same fashion. 287 * 288 * @param b destination buffer. 289 * @param off offset at which to start storing bytes. 290 * @param len maximum number of bytes to read. 291 * @return the number of bytes read, or <code>-1</code> if the end of 292 * the stream has been reached. 293 * @exception IOException if this input stream has been closed by 294 * invoking its {@link #close()} method, 295 * or an I/O error occurs. 296 */ 297 public synchronized int read(byte b[], int off, int len) 298 throws IOException 299 { 300 getBufIfOpen(); // Check for closed stream 301 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { 302 throw new IndexOutOfBoundsException(); 303 } else if (len == 0) { 304 return 0; 305 } 306 307 int n = 0; 308 for (;;) { 309 int nread = read1(b, off + n, len - n); 310 if (nread <= 0) 311 return (n == 0) ? nread : n; 312 n += nread; 313 if (n >= len) 314 return n; 315 // if not closed but no bytes available, return 316 InputStream input = in; 317 if (input != null && input.available() <= 0) 318 return n; 319 } 320 } 321 322 /** 323 * See the general contract of the <code>skip</code> 324 * method of <code>InputStream</code>. 325 * 326 * @exception IOException if the stream does not support seek, 327 * or if this input stream has been closed by 328 * invoking its {@link #close()} method, or an 329 * I/O error occurs. 330 */ 331 public synchronized long skip(long n) throws IOException { 332 getBufIfOpen(); // Check for closed stream 333 if (n <= 0) { 334 return 0; 335 } 336 long avail = count - pos; 337 338 if (avail <= 0) { 339 // If no mark position set then don't keep in buffer 340 if (markpos <0) 341 return getInIfOpen().skip(n); 342 343 // Fill in buffer to save bytes for reset 344 fill(); 345 avail = count - pos; 346 if (avail <= 0) 347 return 0; 348 } 349 350 long skipped = (avail < n) ? avail : n; 351 pos += skipped; 352 return skipped; 353 } 354 355 /** 356 * Returns an estimate of the number of bytes that can be read (or 357 * skipped over) from this input stream without blocking by the next 358 * invocation of a method for this input stream. The next invocation might be 359 * the same thread or another thread. A single read or skip of this 360 * many bytes will not block, but may read or skip fewer bytes. 361 * <p> 362 * This method returns the sum of the number of bytes remaining to be read in 363 * the buffer (<code>count - pos</code>) and the result of calling the 364 * {@link java.io.FilterInputStream#in in}.available(). 365 * 366 * @return an estimate of the number of bytes that can be read (or skipped 367 * over) from this input stream without blocking. 368 * @exception IOException if this input stream has been closed by 369 * invoking its {@link #close()} method, 370 * or an I/O error occurs. 371 */ 372 public synchronized int available() throws IOException { 373 int n = count - pos; 374 int avail = getInIfOpen().available(); 375 return n > (Integer.MAX_VALUE - avail) 376 ? Integer.MAX_VALUE 377 : n + avail; 378 } 379 380 /** 381 * See the general contract of the <code>mark</code> 382 * method of <code>InputStream</code>. 383 * 384 * @param readlimit the maximum limit of bytes that can be read before 385 * the mark position becomes invalid. 386 * @see java.io.BufferedInputStream#reset() 387 */ 388 public synchronized void mark(int readlimit) { 389 marklimit = readlimit; 390 markpos = pos; 391 } 392 393 /** 394 * See the general contract of the <code>reset</code> 395 * method of <code>InputStream</code>. 396 * <p> 397 * If <code>markpos</code> is <code>-1</code> 398 * (no mark has been set or the mark has been 399 * invalidated), an <code>IOException</code> 400 * is thrown. Otherwise, <code>pos</code> is 401 * set equal to <code>markpos</code>. 402 * 403 * @exception IOException if this stream has not been marked or, 404 * if the mark has been invalidated, or the stream 405 * has been closed by invoking its {@link #close()} 406 * method, or an I/O error occurs. 407 * @see java.io.BufferedInputStream#mark(int) 408 */ 409 public synchronized void reset() throws IOException { 410 getBufIfOpen(); // Cause exception if closed 411 if (markpos < 0) 412 throw new IOException("Resetting to invalid mark"); 413 pos = markpos; 414 } 415 416 /** 417 * Tests if this input stream supports the <code>mark</code> 418 * and <code>reset</code> methods. The <code>markSupported</code> 419 * method of <code>BufferedInputStream</code> returns 420 * <code>true</code>. 421 * 422 * @return a <code>boolean</code> indicating if this stream type supports 423 * the <code>mark</code> and <code>reset</code> methods. 424 * @see java.io.InputStream#mark(int) 425 * @see java.io.InputStream#reset() 426 */ 427 public boolean markSupported() { 428 return true; 429 } 430 431 /** 432 * Closes this input stream and releases any system resources 433 * associated with the stream. 434 * Once the stream has been closed, further read(), available(), reset(), 435 * or skip() invocations will throw an IOException. 436 * Closing a previously closed stream has no effect. 437 * 438 * @exception IOException if an I/O error occurs. 439 */ 440 public void close() throws IOException { 441 byte[] buffer; 442 while ( (buffer = buf) != null) { 443 if (bufUpdater.compareAndSet(this, buffer, null)) { 444 InputStream input = in; 445 in = null; 446 if (input != null) 447 input.close(); 448 return; 449 } 450 // Else retry in case a new buf was CASed in fill() 451 } 452 } 453 }
-----------------------------------------------------------------------------------
BufferedOutputStream
类声明:public class BufferedOutputStream extends FilterOutputStream
明白了BufferedInputStream后就很好理解BufferedOutputStream了,在BufferedOutputStream内部也提供了一个缓冲区,当缓冲区中的数据满了以后或者直接调用flush()方法就会把缓冲区中的数据写入到输出流。直接查看源代码就明白了。
1 package java.io; 2 3 /** 4 * The class implements a buffered output stream. By setting up such 5 * an output stream, an application can write bytes to the underlying 6 * output stream without necessarily causing a call to the underlying 7 * system for each byte written. 8 * 9 * @author Arthur van Hoff 10 * @since JDK1.0 11 */ 12 public 13 class BufferedOutputStream extends FilterOutputStream { 14 /** 15 * The internal buffer where data is stored. 16 */ 17 protected byte buf[]; 18 19 /** 20 * The number of valid bytes in the buffer. This value is always 21 * in the range <tt>0</tt> through <tt>buf.length</tt>; elements 22 * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid 23 * byte data. 24 */ 25 protected int count; 26 27 /** 28 * Creates a new buffered output stream to write data to the 29 * specified underlying output stream. 30 * 31 * @param out the underlying output stream. 32 */ 33 public BufferedOutputStream(OutputStream out) { 34 this(out, 8192); 35 } 36 37 /** 38 * Creates a new buffered output stream to write data to the 39 * specified underlying output stream with the specified buffer 40 * size. 41 * 42 * @param out the underlying output stream. 43 * @param size the buffer size. 44 * @exception IllegalArgumentException if size <= 0. 45 */ 46 public BufferedOutputStream(OutputStream out, int size) { 47 super(out); 48 if (size <= 0) { 49 throw new IllegalArgumentException("Buffer size <= 0"); 50 } 51 buf = new byte[size]; 52 } 53 54 /** Flush the internal buffer */ 55 private void flushBuffer() throws IOException { 56 if (count > 0) { 57 out.write(buf, 0, count); 58 count = 0; 59 } 60 } 61 62 /** 63 * Writes the specified byte to this buffered output stream. 64 * 65 * @param b the byte to be written. 66 * @exception IOException if an I/O error occurs. 67 */ 68 public synchronized void write(int b) throws IOException { 69 if (count >= buf.length) { 70 flushBuffer(); 71 } 72 buf[count++] = (byte)b; 73 } 74 75 /** 76 * Writes <code>len</code> bytes from the specified byte array 77 * starting at offset <code>off</code> to this buffered output stream. 78 * 79 * <p> Ordinarily this method stores bytes from the given array into this 80 * stream's buffer, flushing the buffer to the underlying output stream as 81 * needed. If the requested length is at least as large as this stream's 82 * buffer, however, then this method will flush the buffer and write the 83 * bytes directly to the underlying output stream. Thus redundant 84 * <code>BufferedOutputStream</code>s will not copy data unnecessarily. 85 * 86 * @param b the data. 87 * @param off the start offset in the data. 88 * @param len the number of bytes to write. 89 * @exception IOException if an I/O error occurs. 90 */ 91 public synchronized void write(byte b[], int off, int len) throws IOException { 92 if (len >= buf.length) { 93 /* If the request length exceeds the size of the output buffer, 94 flush the output buffer and then write the data directly. 95 In this way buffered streams will cascade harmlessly. */ 96 flushBuffer(); 97 out.write(b, off, len); 98 return; 99 } 100 if (len > buf.length - count) { 101 flushBuffer(); 102 } 103 System.arraycopy(b, off, buf, count, len); 104 count += len; 105 } 106 107 /** 108 * Flushes this buffered output stream. This forces any buffered 109 * output bytes to be written out to the underlying output stream. 110 * 111 * @exception IOException if an I/O error occurs. 112 * @see java.io.FilterOutputStream#out 113 */ 114 public synchronized void flush() throws IOException { 115 flushBuffer(); 116 out.flush(); 117 } 118 }