緩沖字節流BufferedInputStream的使用及原理分析


我們平時常常會對文件進行讀取操作,如使用FileInputStream進行讀取操作,則效率很低.為此我們可以使用緩沖字節流BufferedInputStream來操作,讀取的效率會有很大的提升.在此我們介紹如何使用BufferedInputStream及分析其工作的原理.

一.使用介紹:

1.1定義:

BufferedInputStream是高級流,不能直接對文件進行操作,只有低級流才能直接與文件相連,所以需套接一個低級流,例如:

FileInputStream fis = new FileInputStream("test.txt"); BufferedInputStream bis = new BufferedInputStream(fis);

 

1.2讀取文件:

讀取時,我們一般使用read()方法循環的方式讀取,如讀取到了文件末尾,則read()方法會返回-1,例如:

 

int len = -1; while((len=bis.read())!=-1){ System.out.println(len); }

 

 

 

通過循環讀取的方式,可以將文件讀取完畢.

 

二.原理分析:

 通過以上內容了解了BufferedInputStream的使用方式,那其工作原理是如何的呢?下面將通過BufferedInputStream的源碼分析來了解其工作的原理.

2.1首先分析下BufferedInputStream的屬性和構造函數:

屬性如下:

//默認的緩沖大小8k
private static int DEFAULT_BUFFER_SIZE = 8192;
/*最大的緩沖大小Integer.MAX_VALUE - 8,減8是由於虛擬機中在數組中保留了一些頭信息*/
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
//定義數據存儲的緩沖字節數組
protected volatile byte buf[];
//原子屬性更新器,用來保證對buf進行原子更新
private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");
//buf字節數組中實際數據的大小
protected int count;
//開始讀取的位置
protected int pos;
//記錄最后一次開始讀取的位置
protected int markpos = -1;
//允許的最大提前讀取量
protected int marklimit;

 

構造函數如下:

//以InputStream作為參數,緩沖區大小默認8k
public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
}

//自定義緩沖區大小
public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
}

 

2.2方法分析:

2.2.1.read()方法分析:

//無參數的read()方法
public synchronized int read() throws IOException {
        //如果開始讀取的位置大於或等於緩沖區實際大小
        if (pos >= count) {
            //則填充緩沖區
            fill();
            //填充之后,讀取位置還是大於或等於緩沖區實際大小,則讀取完畢,返回-1
            if (pos >= count)
                return -1;
        }
        //返回緩沖區中的第一個字節
        return getBufIfOpen()[pos++] & 0xff;
}

 

 2.2.2.read1(byte[] b, int off, int len)方法分析:

此方法可以自定義的字節數組,以及開始讀取的位置和實際讀取的長度,源碼如下:

 

private int read1(byte[] b, int off, int len) throws IOException {
     //可讀取的大小 
        int avail = count - pos;
        //如果可讀取大小小於或等於0
        if (avail <= 0) {
             //如果len大於或等於緩沖區大小且標記位置小於0,則按照給定的長度讀取
             if (len >= getBufIfOpen().length && markpos < 0) {
                 return getInIfOpen().read(b, off, len);
             }
            //否則按照默認大小讀取
            fill();
             avail = count - pos;
             //如果可讀取大小小於或等於,則讀取完畢,返回-1
             if (avail <= 0) return -1;
         }
         //取avail和len之間的較小值
         int cnt = (avail < len) ? avail : len;
         //將緩沖區的字節數組從pos位置開始,長度為cnt的內容復制到b字節數組中off開始的位置
         System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
         //讀取位置增加cnt
         pos += cnt;
         //返回讀取的長度
         return cnt;
}

 

 

2.2.3.read(byte b[], int off, int len)方法分析:

public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        //檢查流是否關閉
        getBufIfOpen(); // Check for closed stream
       //通過"|"運算確保off,len大於或等於0,b.length大於或等於off+len
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }
        int n = 0;
        //無限循環,只要剩余的內容滿足長度len,則保證每次能讀取到的長度是len
        for (;;) {
            //讀取len長度字節
            int nread = read1(b, off + n, len - n);
            if (nread <= 0)
                return (n == 0) ? nread : n;
            //因為緩沖字節數組的長度是8192,假設len是800,則讀取10次之后,緩沖字節剩余的長度是192,則第一次讀取的長度就是192,n也就是192,此時,不會返回,會再次循環.再次循環時,則先將填充緩沖區,再讀取剩余的608,讀取到了800之后,則返回800的長度.
            n += nread;
            if (n >= len)
                return n;
            // if not closed but no bytes available, return
            InputStream input = in;
            //如果輸入流中緩沖去可用的大小小於或等於0,則返回n
            if (input != null && input.available() <= 0)
                return n;
        }
}

 

2.2.4.fill()方法分析:

fill()方法是用於讀取數據並填充緩沖區.

private void fill() throws IOException {
        //檢查流是否關閉
        byte[] buffer = getBufIfOpen();
        //判斷標記位置小於0
        if (markpos < 0)
            //設置位置為0,即開始位置
            pos = 0;            /* no mark: throw away the buffer */
        //如果位置大於或等於緩沖區大小,則按如下邏輯處理
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* 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 * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                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;
            }
        //設置大小為pos
        count = pos;
        //讀取長度為默認大小的數據到緩沖區,並返回讀取的長度
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            //將count設置為長度大小
            count = n + pos;
} 

 


免責聲明!

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



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