首页 > 技术文章 > Java字节流:BufferedInputStream BufferedOutputStream

xinhuaxuan 2016-11-14 20:15 原文

-----------------------------------------------------------------------------------
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     }
View Code

当此时从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&nbsp;- 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 }
View Code

-----------------------------------------------------------------------------------
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 &lt;= 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 }

 

推荐阅读