java原始碼-BufferedReader

weixin_34292287發表於2018-09-12

開篇

 在設計模式中有一種叫做裝飾者模式,剛好BufferedReader的原始碼是這個設計模式的最好例子,一併看下原始碼。


原始碼分析

建構函式

  • BufferedReader的類變數的Reader in 用以建構函式引數中的Reader in引數,BufferedReader的所有讀寫操作都通過Reader物件進行操作。
  • BufferedReader相當於針對內部的Reader物件進行了一層包裝,可以理解為裝飾者。
public class BufferedReader extends Reader {
    private Reader in;
    private char cb[];
    //nextChar代表下次要讀取的位置,nChars表示總共的字元個數
    private int nChars, nextChar;

    private static final int INVALIDATED = -2;
    private static final int UNMARKED = -1;
    private int markedChar = UNMARKED;
    private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
    private boolean skipLF = false;
    private boolean markedSkipLF = false;
    private static int defaultCharBufferSize = 8192;
    private static int defaultExpectedLineLength = 80;

    public BufferedReader(Reader in, int sz) {
        super(in);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.in = in;
        cb = new char[sz];
        nextChar = nChars = 0;
    }

    public BufferedReader(Reader in) {
        this(in, defaultCharBufferSize);
    }
}



載入資料

  • 負責通過Reader in物件讀取字元到指定數量的字元資料到cb陣列當中。
  • dst表示儲存資料的起始位置,cb.length-dst表示讀取字元的個數。
  • 在執行read和readline操作的之前如果cb當中可讀字元不足會先執行fill()讀取字元。
 private void fill() throws IOException {
        int dst;
        if (markedChar <= UNMARKED) {
            /* No mark */
            dst = 0;
        } else {
            // 省略一部分程式碼
        }

        int n;
        do {
            // 從底層input讀取資料到cb,cb中起始位置是dst, 
            // 讀取的長度是cb的lenght減去起始位置dst,理解剩餘能夠裝的字元
            n = in.read(cb, dst, cb.length - dst);
        } while (n == 0);
        if (n > 0) {
            // 設定最大可讀字元結束位置
            nChars = dst + n;
            // 設定可讀字元的起始位置
            nextChar = dst;
        }
    }



read過程

  • 讀取過程中如果滿足條件(nextChar 表示下一個讀取字元>= nChars可用字元),代表字元已經讀取完畢那麼就通過fill()進行預載入。
  • 讀取當前字元並累加當前可讀取字元,執行nextChar++操作。
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            for (;;) {
                if (nextChar >= nChars) {
                    fill();
                    if (nextChar >= nChars)
                        return -1;
                }
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                        continue;
                    }
                }
                // 讀取當前字元並累加下一個讀取位置
                return cb[nextChar++];
            }
        }
    }



readline過程

  • 讀取過程中如果滿足條件(nextChar 表示下一個讀取字元>= nChars可用字元),代表字元已經讀取完畢那麼就通過fill()進行預載入。
  • 讀取過程中如果遇到\r\n則中斷迴圈,通過str = new String(cb, startChar, i - startChar)返回整行資料。
    String readLine(boolean ignoreLF) throws IOException {
        //讀取的資料最終放在這個s中,
        StringBuffer s = null;
        int startChar;

        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;

        bufferLoop:
            for (;;) {

                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        //從這裡返回,可能是因為讀取的資料最後沒有以\n或\r結束
                        return s.toString();
                    else
                        // 從這裡返回,是因為開始讀的時候,就已經是input的末尾了,
                        // 所以s本身就沒有被初始化,只能返回null
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;

                /* Skip a leftover '\n', if necessary */
                if (omitLF && (cb[nextChar] == '\n'))
                    nextChar++;
                skipLF = false;
                omitLF = false;

            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == '\n') || (c == '\r')) {
                        eol = true;
                        break charLoop;
                    }
                }

                startChar = nextChar;
                nextChar = i;

                if (eol) {
                    String str;
                    // 執行到這,s為null,說明是第一次迴圈中就讀到了行尾。
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        // 執行到這,起碼說明是第二次迴圈了,s裡已經有了第一次讀取的資料
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == '\r') {
                        skipLF = true;
                    }
                    // 執行到這說明讀到了行尾,返回str
                    return str;
                }

                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                // 執行到這說明,讀取了整個cb的資料,發現一直沒有\n或者\r, 
                // 之後回到最初迴圈繼續讀取。
                s.append(cb, startChar, i - startChar);
            }
        }
    }


類依賴圖

  • BufferedReader的類依賴圖如下圖,所有的io reader都是繼承自Reader作為基類。


    6302559-fdc3845cb11ae067.png


裝飾設計模式

裝飾設計模式:javaIO技術中的裝飾設計模式,對一組物件的功能進行增強時,就可以使用該設計模式

相關文章