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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM