BufferedInputStream-我們到底能走多遠系列(3)


我們到底能走多遠系列(3)

扯淡:

現在項目里的java代碼,都是封裝,封裝,再封裝。在沒有空閑的趕工編碼,幾年后會感覺學不動,畢竟少了很多思考的時間。也基本不會去研究代碼的底層實現。時間一長就會覺得自己什么也不會。

一個大型的項目,一般不可能只用java實現,可能會用到C,C++,shell,python等等。單單一個web就要學很多,jsp,jquery,javascript,html,css,各種開源的不開源的框架,各種web服務器,數據庫等等等。java程序員的確容易迷茫,但精通一樣吧,怕找不到工作,全學吧,怕一直是碼農。

其實,我覺得大多數的人都只是希望能做到“工程師”,解決問題的人。我看到現在接近40的程序員也還是蠻吃香的,他們的能力也是這樣慢慢碼過來的。和他們交流的時候感覺他們什么都知道點,也有自己及其精通的一面。這個方向似乎也是一種不錯的職業規划。

-----------------------------------------------------------------------------

總之,不要停止學習,不要停止進步。

-----------------------------------------------------------------------------

BufferedInputStream應該比較實用吧。讀讀源碼,學習下。

繼承結構:

BufferedInputStream ----> FilterInputStream -----> InputStream

概述:

FilterInputStream繼承自InputStream屬於輸入流中的鏈接流,同時引用了InputStream,將 InputStream封裝成一個內部變量,同時構造方法上需要傳入一個InputStream。這是一個典型的裝飾器模式,他的任何子類都可以對一個繼 承自InputStream的原始流或其他鏈接流進行裝飾,如我們常用的使用BufferedInputStream對FileInputStream進 行裝飾,使普通的文件輸入流具備了內存緩存的功能,通過內存緩沖減少磁盤io次數。

BufferedInputStream方法一覽:

private void fill()
private byte[] getBufIfOpen()
private InputStream getInIfOpen()
private int read1(byte[] b, int off, int len)
-------------------------------------------------
public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
--------------------------------------------------
public synchronized int available()
返回此輸入流方法的下一個調用方可以不受阻塞地從此輸入流讀取(或跳過)的字節數。
public void close()
關閉此輸入流並釋放與該流關聯的所有系統資源。
public synchronized void mark(int readlimit)
在此輸入流中標記當前的位置。對 reset 方法的后續調用會在最后標記的位置重新定位此流,以便后續讀取重新讀取相同的字節。 
readlimit - 在標記位置失效前可以讀取字節的最大限制。
public boolean markSupported()
測試此輸入流是否支持 mark 和 reset 方法。
public synchronized int read()
從輸入流讀取下一個數據字節。返回 0 到 255 范圍內的 int 字節值。如果因已到達流末尾而沒有可用的字節,則返回值 -1。
public synchronized int read(byte b[], int off, int len)
將輸入流中最多 len 個數據字節讀入字節數組。嘗試讀取多達 len 字節,但可能讀取較少數量。以整數形式返回實際讀取的字節數。 
public synchronized void reset()
將此流重新定位到對此輸入流最后調用 mark 方法時的位置。
public synchronized long skip(long n)
跳過和放棄此輸入流中的 n 個數據字節。

源碼:

package java.io;
 
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
//繼承FilterInputStream,FilterInputStream繼承InputStream
//該類主要完成對被包裝流,加上一個緩存的功能,所謂的緩存不就是new一個byte數組
public class BufferedInputStream extends FilterInputStream {
    private static int defaultBufferSize = 8192;                 //默認緩存的大小
    protected volatile byte buf[];    //內部的緩存(數組)volatile修飾,保證不同的線程總是看到某個成員變量的同一個值
    protected int count; //buffer的大小,表示當前緩沖區內總共有多少有效數據
    protected int pos; //buffer中cursor的位置,即byte數組的當前下標,下次讀取從該位置讀取
    protected int markpos = -1;                                 //mark的位置
    protected int marklimit;    //mark的范圍,最多能mark的字節長度,也就是從mark位置到當前pos的最大長度,作為參數傳入
 
//原子性更新。和一致性編程相關
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class,  byte[].class, "buf");
 
    private InputStream getInIfOpen() throws IOException {  //檢查輸入流是否關閉,同時返回被包裝流
        InputStream input = in;
     if (input == null)    throw new IOException("Stream closed");
        return input;
    }
 
    private byte[] getBufIfOpen() throws IOException {             //檢查buffer的狀態,同時返回緩存
        byte[] buffer = buf;
     if (buffer == null)   throw new IOException("Stream closed");         //不太可能發生的狀態
        return buffer;
    }
 
    public BufferedInputStream(InputStream in) {                //構造器
         this(in, defaultBufferSize);    //指定默認長度(defaultBufferSize)為buffer的長度
    }
 
    public BufferedInputStream(InputStream in, int size) {            //構造器
         super(in);
        if (size <= 0) {    //檢查size參數,
            throw new IllegalArgumentException("Buffer size <= 0");
        }
         buf = new byte[size]; //創建指定長度的buffer,這就緩存!
    }
 
//從流中讀取數據,填充如緩存中。構造函數開辟了緩存,都是空值,需要方法來填充它們。
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();             //得到buffer
//buffer沒有被mark,也就是說沒有被調用mark方法
     if (markpos < 0) 
         pos = 0; //mark位置小於0,此時pos為0
    // buffer被mark的情況
     else if (pos >= buffer.length) //pos大於buffer的長度,讀到buffer的最后
         if (markpos > 0) {    //有mark,將mark-pos段的數組保留
         int sz = pos - markpos;            
         System.arraycopy(buffer, markpos, buffer, 0, sz);
         pos = sz;
         markpos = 0;
         } else if (buffer.length >= marklimit) {    //buffer的長度大於marklimit時,mark失效
         markpos = -1;                        
         pos = 0;                             //丟棄buffer中的內容
         } else {//buffer的長度小於marklimit時對buffer擴容
         int nsz = pos * 2;
         if (nsz > marklimit)         nsz = marklimit;//擴容為原來的2倍,太大則為marklimit大小
         byte nbuf[] = new byte[nsz];            
         System.arraycopy(buffer, 0, nbuf, 0, pos);     //將buffer中的字節拷貝如擴容后的buf中
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {//在buffer在被操作時,不能取代此buffer
                    throw new IOException("Stream closed");
                }
                buffer = nbuf; //將新buf賦值給buffer
         }
        count = pos;
     int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)     count = n + pos;
    }
 
    public synchronized int read() throws IOException { //讀取下一個字節
     if (pos >= count) {                             //到達buffer的末端
         fill();//就從流中讀取數據,填充buffer
         if (pos >= count)     return -1; //讀過一次,沒有數據則返回-1
     }
     return getBufIfOpen()[pos++] & 0xff;    //返回buffer中下一個位置的字節
    }
 
    private int read1(byte[] b, int off, int len) throws IOException {    //將數據從流中讀入buffer中
     int avail = count - pos;    //buffer中還剩的可讀字符
     if (avail <= 0) {//buffer中沒有可以讀取的數據時
         if (len >= getBufIfOpen().length && markpos < 0) {    
         return getInIfOpen().read(b, off, len); //將輸入流中的字節讀入b中
         }
         fill();//填充
         avail = count - pos;
         if (avail <= 0) return -1;
     }
     int cnt = (avail < len) ? avail : len; //從流中讀取后,檢查可以讀取的數目
     System.arraycopy(getBufIfOpen(), pos, b, off, cnt);    //將當前buffer中的字節放入b的末端
     pos += cnt;
     return cnt;
    }
 
 
// byte b[]調用者提供,調用者要使用的也是它。
    public synchronized int read(byte b[], int off, int len)throws IOException {
        getBufIfOpen();                                      // 檢查buffer是否open
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {//檢查輸入參數是否正確
         throw new IndexOutOfBoundsException();
     } else if (len == 0) {
            return 0;
        }
     int n = 0;
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0)     return (n == 0) ? 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() <= 0)     return n;
        }
    }
 
 
    public synchronized long skip(long n) throws IOException {
        getBufIfOpen();                      // 檢查buffer是否關閉
     if (n <= 0) {    return 0;     }         //檢查輸入參數是否正確
     long avail = count - pos;              //buffered中可以讀取字節的數目
        if (avail <= 0) {                     //可以讀取的小於0,則從流中讀取
            if (markpos <0)  return getInIfOpen().skip(n); //mark小於0,則mark在流中      
            fill();                         // 從流中讀取數據,填充緩沖區。
            avail = count - pos;                 //可以讀的取字節為buffer的容量減當前位置
            if (avail <= 0)     return 0;
        }       
        long skipped = (avail < n) ? avail : n;    
        pos += skipped;                     //當前位置改變
        return skipped;
    }
 
    public synchronized int available() throws IOException {
         return getInIfOpen().available() + (count - pos);        
    }
     //該方法不會block!返回流中可以讀取的字節的數目。
     //該方法的返回值為緩存中的可讀字節數目加流中可讀字節數目的和
 
    public synchronized void mark(int readlimit) {     //當前位置處為mark位置
     marklimit = readlimit;
     markpos = pos;
    }
 
    public synchronized void reset() throws IOException {
        getBufIfOpen(); // 緩沖去關閉了,肯定就拋出異常!程序設計中經常的手段
         if (markpos < 0)        throw new IOException("Resetting to invalid mark");
         pos = markpos;
    }
 
    public boolean markSupported() {         //該流和ByteArrayInputStream一樣都支持mark
         return true;
    }
 
     //關閉當前流同時釋放相應的系統資源。
    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
        }
    }
}

 

fill()---> 從字面理解就是填充的意思,實際上是從真正的輸入流中讀取一些新數據放入緩沖內存中,之后直到緩沖內存中的數據讀完前都不會再從真正的流中讀取數據。
網上對這個方法的分析:
          普通的情況:

  

          有mark的情況:

 

普通的練習,復制文件:

first:

private void copyFile(String fromPath, String toPath) throws IOException{
        // input
        File fromFile = new File(fromPath);
        InputStream is = new FileInputStream(fromFile);
        BufferedInputStream bis = new BufferedInputStream(is);
        // output
        File toFile = new File(toPath);
        OutputStream os = new FileOutputStream(toFile);
        BufferedOutputStream bos = new BufferedOutputStream(os);
        // transfer station 
        byte b[] = new byte[(int)fromFile.length()] ;
        while(bis.read(b, 0, b.length) != -1){
            bos.write(b, 0, b.length);
        }
        bis.close();
        bos.close();
    }

second:

public static void copy()   throws IOException  
    {  
        BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("d:/我的文檔/123.txt"));  
        BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:/我的文檔/txt.txt"));  
  
        int by = 0;  
        while ((by=bufis.read()) != -1)  
        {  
            bufos.write(by);  
        }  
  
        bufos.close();  
        bufis.close();  
    }

總結:

理解還不是很深刻,對它的實現的理解還有很多不理解的地方,還需要繼續學習。

加油吧!

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不會成功。
共勉。

 


免責聲明!

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



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