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