使用方法
BufferedInputStream繼承于FilterInputStream,提供緩沖輸入流功能。緩沖輸入流相對于普通輸入流的優勢是,它提供了一個緩沖數組,每次調用read方法的時候,它首先嘗試從緩沖區裡讀取資料,若讀取失敗(緩沖區無可讀資料),則選擇從實體資料源(譬如檔案)讀取新資料(這裡會嘗試盡可能讀取多的位元組)放入到緩沖區中,最後再将緩沖區中的内容部分或全部傳回給使用者.由于從緩沖區裡讀取資料遠比直接從實體資料源(譬如檔案)讀取速度快。
方法介紹
BufferedInputStream提供的API如下:
//構造方法
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in, int size)
//下一位元組是否可讀
synchronized int available()
//關閉
void close()
//标記, readlimit為mark後最多可讀取的位元組數
synchronized void mark(int readlimit)
//是否支援mark, true
boolean markSupported()
//讀取一個位元組
synchronized int read()
//讀取多個位元組到b
synchronized int read(byte[] b, int off, int len)
//重置會mark位置
synchronized void reset()
//跳過n個位元組
synchronized long skip(long n)
使用示例
public void testBufferedInput() {
try {
/**
* 建立輸入流 BufferedInputStream, 緩沖區大小為8
* buffer.txt内容為
* abcdefghij
*/
InputStream in = new BufferedInputStream(new FileInputStream(new File("buff.txt")), );
/*從位元組流中讀取5個位元組*/
byte [] tmp = new byte[];
in.read(tmp, , );
System.out.println("位元組流的前5個位元組為: " + new String(tmp));
/*标記測試*/
in.mark();
/*讀取5個位元組*/
in.read(tmp, , );
System.out.println("位元組流中第6到10個位元組為: " + new String(tmp));
/*reset*/
in.reset();
System.out.printf("reset後讀取的第一個位元組為: %c" , in.read());
} catch (Exception e) {
e.printStackTrace();
}
}
運作結果如下:
位元組流的前個位元組為: abcde
位元組流中第到個位元組為: fghij
reset後讀取的第一個位元組為: f
源碼分析
構造方法
BufferedInputStream的構造方法有兩個,差別是緩沖區大小設定。
/**
* Creates a <code>BufferedInputStream</code>
* and saves its argument, the input stream
* <code>in</code>, for later use. An internal
* buffer array is created and stored in <code>buf</code>.
*
* @param in the underlying input stream.
*/
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE); //預設8192, 8M
}
/**
* Creates a <code>BufferedInputStream</code>
* with the specified buffer size,
* and saves its argument, the input stream
* <code>in</code>, for later use. An internal
* buffer array of length <code>size</code>
* is created and stored in <code>buf</code>.
*
* @param in the underlying input stream.
* @param size the buffer size.
* @exception IllegalArgumentException if {@code size <= 0}.
*/
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= ) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
read方法
read方法有每次讀取一個位元組和一次讀取多個位元組兩種重載。下面主要分析讀取多個位元組的read方法。重點在于fill()方法。
/**
* Reads bytes from this byte-input stream into the specified byte array,
* starting at the given offset.
*
* <p> This method implements the general contract of the corresponding
* <code>{@link InputStream#read(byte[], int, int) read}</code> method of
* the <code>{@link InputStream}</code> class. As an additional
* convenience, it attempts to read as many bytes as possible by repeatedly
* invoking the <code>read</code> method of the underlying stream. This
* iterated <code>read</code> continues until one of the following
* conditions becomes true: <ul>
*
* @param b destination buffer.
* @param off offset at which to start storing bytes.
* @param len maximum number of bytes to read.
* @return the number of bytes read, or <code>-1</code> if the end of the stream has been reached.
* @exception IOException if this input stream has been closed by invoking its {@link #close()} method,
* or an I/O error occurs.
*/
public synchronized int read(byte b[], int off, int len)
throws IOException
{
getBufIfOpen(); // Check for closed stream
if ((off | len | (off + len) | (b.length - (off + len))) < ) {
throw new IndexOutOfBoundsException();
} else if (len == ) {
return ;
}
int n = ;
for (;;) {
int nread = read1(b, off + n, len - n); //讀取len長度的位元組到b中
if (nread <= )
return (n == ) ? nread : n;
n += nread;
if (n >= len)
return n;
// if not closed but no bytes available, return
InputStream input = in;
if (input != null && input.available() <= )
return n;
}
}
/**
* Check to make sure that buffer has not been nulled out due to
* close; if not return it;
*/
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
/**
* Read characters into a portion of an array, reading from the underlying
* stream at most once if necessary.
*/
private int read1(byte[] b, int off, int len) throws IOException {
int avail = count - pos; //緩沖區中可讀位元組數
if (avail <= ) { //沒可讀位元組
/* If the requested length is at least as large as the buffer, and
if there is no mark/reset activity, do not bother to copy the
bytes into the local buffer. In this way buffered streams will
cascade harmlessly. */
if (len >= getBufIfOpen().length && markpos < ) { //沒mark并且請求長度大于buff長度
return getInIfOpen().read(b, off, len); //直接從檔案中讀取,不走緩沖區
}
fill(); //修改或者擴充緩沖區
avail = count - pos; //可讀位元組數
if (avail <= ) return -;
}
int cnt = (avail < len) ? avail : len; //取最小值, 緩沖區中可能沒有足夠可讀的位元組
System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //複制
pos += cnt;
return cnt;
}
/**
* Fills the buffer with more data, taking into account
* shuffling and other tricks for dealing with marks.
* Assumes that it is being called by a synchronized method.
* This method also assumes that all data has already been read in,
* hence pos > count.
*/
private void fill() throws IOException {
/**
* 填充字元時如果沒有mark标記, 則直接清空緩沖區,然後将輸入流的資料寫入緩沖區
* 如果有mark标記,則分如下幾種情況
* 1 普通mark,直接将标記以前的字元用标記以後的字元覆寫,剩餘的空間讀取輸入流的内容填充
* 2 目前位置pos >= buffer的長度 >= marklimit,說明mark已經失效,直接清空緩沖區,然後讀取輸入流内容
* 3 buffer長度超出限制,抛出異常
* 4 marklimit比buffer的長度還大,此時mark還沒失效,則擴大buffer空間
*/
byte[] buffer = getBufIfOpen();
if (markpos < )
pos = ; /* no mark: throw away the buffer */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > ) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, , sz);
pos = sz;
markpos = ;
} else if (buffer.length >= marklimit) {
markpos = -; /* buffer got too big, invalidate mark */
pos = ; /* drop buffer contents */
} else if (buffer.length >= MAX_BUFFER_SIZE) {
throw new OutOfMemoryError("Required array size too large");
} else { /* grow buffer */
int nsz = (pos <= MAX_BUFFER_SIZE - pos)
pos * : MAX_BUFFER_SIZE; //擴大後的大小
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, , nbuf, , pos); //将buffer的資料複制到nbuf中
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
throw new IOException("Stream closed");
}
buffer = nbuf; //修改緩沖區
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos); //讀取輸入流中内容填充緩沖區
if (n > )
count = n + pos;
}
mark\reset方法
/**
* See the general contract of the <code>mark</code>
* method of <code>InputStream</code>.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see java.io.BufferedInputStream#reset()
*/
public synchronized void mark(int readlimit) {
marklimit = readlimit;
markpos = pos;
}
/**
* See the general contract of the <code>reset</code>
* method of <code>InputStream</code>.
* <p>
* If <code>markpos</code> is <code>-1</code>
* (no mark has been set or the mark has been
* invalidated), an <code>IOException</code>
* is thrown. Otherwise, <code>pos</code> is
* set equal to <code>markpos</code>.
*
* @exception IOException if this stream has not been marked or,
* if the mark has been invalidated, or the stream
* has been closed by invoking its {@link #close()}
* method, or an I/O error occurs.
* @see java.io.BufferedInputStream#mark(int)
*/
public synchronized void reset() throws IOException {
getBufIfOpen(); // Cause exception if closed
if (markpos < )
throw new IOException("Resetting to invalid mark");
pos = markpos;
}
參考:
[1] http://zhhphappy.iteye.com/blog/1562427
[2] http://blog.sina.com.cn/s/blog_67f995260101huxz.html
[3] http://www.cnblogs.com/skywang12345/p/io_12.html