Java IO筆記(BufferedReader/BufferedWriter)


(最近剛來到簡書平臺,以前在CSDN上寫的一些東西,也在逐漸的移到這兒來,有些篇幅是很早的時候?qū)懴碌?,因此可能會看到一些?nèi)容雜亂的文章,對此深感抱歉,以下為正文)


正文

本篇講述的是Java IO包中的BufferedReader和BufferedWriter。從名字中可以看出它們分別是Reader和Writer的子類,它們的特點(diǎn)是在對流進(jìn)行讀寫操作時,內(nèi)置了緩存區(qū),通過減少與磁盤之間IO操作的次數(shù),從而提升了讀寫效率,下面我們來簡要的看看它們的源碼。
BufferedReader.java

package java.io;
 
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
 
public class BufferedReader extends Reader {
    //內(nèi)置了一個Reader對象句柄in,用于接收傳入的Reader對象。
    private Reader in;
    //定義了一個char類型的數(shù)組,作為內(nèi)置的數(shù)據(jù)存儲區(qū),默認(rèn)大小為8k。
    private char cb[];
    //聲明了兩個int型變量nChars和nextChar,nChars表示緩存區(qū)中存在的字符數(shù)據(jù)的個數(shù),nextChar表示下一個要讀取的字符內(nèi)容在緩存區(qū)中所在的位置。
    private int nChars, nextChar;
    //定義了兩個常量值分別代表了流內(nèi)標(biāo)記的兩種狀態(tài),INVALIDATED表示流中曾經(jīng)有過標(biāo)記,但是超過了標(biāo)記的限制長度,標(biāo)記失效,UNMARKED則是沒有做過標(biāo)記。
    private static final int INVALIDATED = -2;
    private static final int UNMARKED = -1;
    //聲明了一個int型變量markedChar,該變量表示了當(dāng)前流中的標(biāo)記狀態(tài),初始化默認(rèn)值為UNMARKED。
    private int markedChar = UNMARKED;
    //聲明了一個int型變量readAheadLimit,該值是流中標(biāo)記的限制長度,如果標(biāo)記位置超過了該值,則標(biāo)記會被拋棄。該值只有大于零時,才起作用。
    private int readAheadLimit = 0;
 
    //聲明了一個boolean型變量skipLF,該值用于表示是否忽略換行標(biāo)記,初始化默認(rèn)值為false,表示不忽略。
    private boolean skipLF = false;
    //聲明了一個boolean型變量markedSkipLF,該值用于保存skipLF的狀態(tài)。
    private boolean markedSkipLF = false;
    //定義了一個int型的變量值defaultCharBufferSize,該值表示內(nèi)置緩存區(qū)中的容量大小,初始化默認(rèn)值為8k。
    private static int defaultCharBufferSize = 8192;
    //定義了一個int型的變量值defaultExpectedLineLength,該值表示了每行中字符數(shù)的長度,初始化默認(rèn)值為80。
    private static int defaultExpectedLineLength = 80;
 
    /**
     * 定義了帶兩個參數(shù)的構(gòu)造函數(shù),第一個參數(shù)為一個Reader型對象,用于給內(nèi)部定義的Reader對象句柄賦值,第二個參數(shù)為一個int型參數(shù)sz,用來初始化內(nèi)部緩存區(qū)的
     * 容量。
     */
    public BufferedReader(Reader in, int sz) {
        //調(diào)用父類Reader中的構(gòu)造方法,將傳入的Reader對象作為鎖對象,為后面的同步操作提供同步鎖。
        super(in);
    //對傳入的參數(shù)進(jìn)行安全檢測,如果sz小于0則拋出對應(yīng)異常。
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
    //將傳入的Reader對象,賦值給內(nèi)部聲明的Reader對象句柄。
        this.in = in;
    //初始化內(nèi)置的緩存數(shù)組cb,通過傳入的參數(shù)sz來確定cb的容量大小。
        cb = new char[sz];
    初始化nextChar,nChars的值,剛開始都為0。
        nextChar = nChars = 0;
    }
 
    /**
     * 定義了一個帶一個參數(shù)的構(gòu)造函數(shù),傳入的參數(shù)類型為一個Reader對象,內(nèi)部實(shí)際是調(diào)用上面的構(gòu)造方法,內(nèi)置緩存區(qū)使用默認(rèn)大小創(chuàng)建,容量為8k。
     */
    public BufferedReader(Reader in) {
        this(in, defaultCharBufferSize);
    }
 
    /** 
     * 該方法用于檢測當(dāng)前流是否關(guān)閉。如果已經(jīng)關(guān)閉,則拋出相應(yīng)的異常。
     */
    private void ensureOpen() throws IOException {
        if (in == null)
            throw new IOException("Stream closed");
    }
 
    /**
     * 該方法是整個類中的核心方法,用于向內(nèi)置的緩存區(qū)中填充數(shù)據(jù)。
     */
    private void fill() throws IOException {
    //定義了一個int型值dst,用來表示內(nèi)置緩存區(qū)中有效數(shù)據(jù)的起始位置。
        int dst;
    //如果markedChar <= UNMARKED,則表示當(dāng)前流中沒有標(biāo)記,因此可以大膽地直接將dst重置為0,后面會對緩存區(qū)進(jìn)行重新填充。
        if (markedChar <= UNMARKED) {
            dst = 0;
        } else {
            //以下情況表示流中存在標(biāo)記情況。//定義了一個int型值delta,用于接收即將讀取的下一個字符位置于流中標(biāo)記位置的差值。
            int delta = nextChar - markedChar;
        //如果delta>=readAheadLimit,表示當(dāng)前標(biāo)記的位置已經(jīng)超過了標(biāo)記長度的限制,那么此時直接將標(biāo)記清空。
            if (delta >= readAheadLimit) {
        //將流標(biāo)記狀態(tài)恢復(fù)為INVALIDATED,表示因?yàn)槌^標(biāo)記長度限制,流標(biāo)記失效。
                markedChar = INVALIDATED;
        //因?yàn)榱鳂?biāo)記失效,所以此時流標(biāo)記的長度限制也歸零。
                readAheadLimit = 0;
        //將內(nèi)部緩存區(qū)的有效數(shù)據(jù)的起始位置歸零。
                dst = 0;
            } else {
        //如果標(biāo)記的限制長度readAheadLimit小于等于內(nèi)置緩存區(qū)的容量,那么此時將內(nèi)置緩存區(qū)中從標(biāo)記處的數(shù)據(jù)復(fù)制到緩存數(shù)組的開頭,同時將標(biāo)記處置為0,
        //此時緩存區(qū)中有效數(shù)據(jù)長度為delta。
                if (readAheadLimit <= cb.length) {
                    System.arraycopy(cb, markedChar, cb, 0, delta);
                    markedChar = 0;
                    dst = delta;
                } else {
            //如果標(biāo)記的限制長度readAheadLimit大于內(nèi)置緩存區(qū)的容量,那么直接擴(kuò)容緩存區(qū)數(shù)組,將其容量直接擴(kuò)展到標(biāo)記的限制值大小,將原先的內(nèi)置緩存
            //區(qū)中自標(biāo)記處開始的內(nèi)容復(fù)制到擴(kuò)容的新數(shù)組處,并將擴(kuò)容后的數(shù)組賦值給內(nèi)置的緩存數(shù)組cb。此時標(biāo)記處重置為0,緩存區(qū)中有效長度為delta。
                    char ncb[] = new char[readAheadLimit];
                    System.arraycopy(cb, markedChar, ncb, 0, delta);
                    cb = ncb;
                    markedChar = 0;
                    dst = delta;
                }
        //此時即將讀取的下一個字符的索引和數(shù)組緩存區(qū)中的有效數(shù)字都等于delta的值。 
                nextChar = nChars = delta;
            }
        }
    
    //通過一個循環(huán),向內(nèi)置的緩存區(qū)中填充數(shù)據(jù)。為nChars,nextChar更新狀態(tài)。
        int n;
        do {
            n = in.read(cb, dst, cb.length - dst);
        } while (n == 0);
        if (n > 0) {
            nChars = dst + n;
            nextChar = dst;
        }
    }
 
    /**
     * 定義了一個read方法,每次讀取一個字符。事實(shí)上是對read1的一個封裝,添加了同步和阻塞等功能。
     */
    public int read() throws IOException {
        synchronized (lock) {
        //調(diào)用ensureOpen方法,確認(rèn)當(dāng)前流是否關(guān)閉。
            ensureOpen();
            for (;;) {
        //如果nextChar>=nChars,表示當(dāng)前緩存區(qū)中的數(shù)據(jù)已讀完,此時需要調(diào)用fill方法重新向緩存區(qū)中填充數(shù)據(jù)。
                if (nextChar >= nChars) {
                    fill();
            //如果緩存區(qū)中更新后,nextChar還是大于等于nChars,那么表示此時文件已讀到末尾,返回-1。
                    if (nextChar >= nChars)
                        return -1;
                }
        //根據(jù)skipLF的值,判斷是否對換行符進(jìn)行處理,如果為true,則跳過換行符。
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                        continue;
                    }
                }
                return cb[nextChar++];
            }
        }
    }
 
    /**
     * 定義了一個帶三個參數(shù)的read方法,一次可以讀取多個字符數(shù)據(jù)。
     */
    private int read1(char[] cbuf, int off, int len) throws IOException {
    //如果nextChar>=nChars,則表示當(dāng)前緩存區(qū)中的內(nèi)容已經(jīng)全部讀完。
        if (nextChar >= nChars) {
        //如果需要讀取的長度大于等于內(nèi)置緩存區(qū)的容量,并且流中沒有標(biāo)記并且對換行符不做處理的時候,那么直接調(diào)用傳入的Reader對象相應(yīng)的read方法,直接從
        //原始數(shù)據(jù)流中讀取指定長度的數(shù)據(jù),避免了先拷貝到緩存區(qū)再從緩存區(qū)中取出的麻煩。
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
                return in.read(cbuf, off, len);
            }
        //向內(nèi)置緩存區(qū)中填充數(shù)據(jù)。
            fill();
        }
    //如果更新完內(nèi)置緩存區(qū)后,nextChar仍然大于等于nChars,則表示文件已經(jīng)讀取完畢,此時返回-1。
        if (nextChar >= nChars) return -1;
    //根據(jù)skipLF的值,判斷是否對換行符進(jìn)行處理,如果為true,則進(jìn)行相應(yīng)處理。
        if (skipLF) {
            skipLF = false;
        //如果下一個讀取的字符為換行符,那么跳過,并檢測下一次讀取是否超過緩存區(qū)容量,如果超過則進(jìn)行緩存區(qū)填充,填充完畢后再次進(jìn)行檢測,如果nextChar
        //仍然大于等于nChars,那么表示文件內(nèi)容已經(jīng)讀完,則返回-1。
            if (cb[nextChar] == '\n') {
                nextChar++;
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars)
                    return -1;
            }
        }
    //定義了一個int型變量n用來存放len,和nChars-nextChar之間的最小值。該值代表著實(shí)際讀取的字符數(shù)量。
        int n = Math.min(len, nChars - nextChar);
    //通過System.arraycopy方法,從內(nèi)置的數(shù)據(jù)緩存區(qū)中向傳入的字符數(shù)組拷貝指定長度的數(shù)據(jù)。
        System.arraycopy(cb, nextChar, cbuf, off, n);
    //nextChat加上實(shí)際讀取的字符數(shù)量,最終返回實(shí)際讀取的字符數(shù)量。
        nextChar += n;
        return n;
    }
 
    /**
     * 定義了帶一個參數(shù)的readLine方法,該方法一次讀取一行數(shù)據(jù),傳入的參數(shù)是一個boolean型數(shù)值,表示是否忽略換行符,該方法最終返回讀取到的字符串。
     */
    String readLine(boolean ignoreLF) throws IOException {
    //聲明了一個StringBuffer對象,用于存儲讀取到的字符數(shù)據(jù)。
        StringBuffer s = null;
        //聲明了一個int型變量startChar,表示數(shù)據(jù)讀取的起始位置。
    int startChar;
 
        synchronized (lock) {
        //檢測流是否關(guān)閉
            ensureOpen();
        //定義了一個boolean型變量omitLF,用于判斷是否負(fù)略換行符,通過對ignoreLF和skipLF兩個值進(jìn)行或操作來得出是否需要忽略換行符。
            boolean omitLF = ignoreLF || skipLF;
    
    //定義了一個循環(huán)來讀取數(shù)據(jù),在循環(huán)外部定義了一個標(biāo)簽bufferLoop,方便跳出循環(huán)。
        bufferLoop:
            for (;;) {
        //如果nextChar >= nChars,則表示讀取的位置超過了數(shù)組緩存區(qū)中容量,那么此時需要向內(nèi)置緩存區(qū)中重新填充數(shù)據(jù)。
                if (nextChar >= nChars)
                    fill();
        //數(shù)據(jù)填充結(jié)束后再次對讀取位置進(jìn)行檢測,如果nextChar仍然大于等于nChars,那么表示文件已經(jīng)讀取完畢。
                if (nextChar >= nChars) {
            //如果次蘇滬杭存儲內(nèi)容的s中有內(nèi)容,則將s轉(zhuǎn)化為字符串并返回,否則返回null。
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
        //定義了一個boolean型變量eol(end of line),該變量用于表示是否是以換行符結(jié)尾。
                boolean eol = false;
                char c = 0;
                int i;
 
                //如果遇到了換行符那么跳過該字符,然后重置skipLF,omitLF狀態(tài)。
                if (omitLF && (cb[nextChar] == '\n'))
                    nextChar++;
                skipLF = false;
                omitLF = false;
        //在循環(huán)中嵌套了一個循環(huán)用于尋找換行符,并添加了一個標(biāo)記charLoop,方便跳出循環(huán)。
            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == '\n') || (c == '\r')) {
                        eol = true;
                        break charLoop;
                    }
                }
 
                startChar = nextChar;
                nextChar = i;
 
        //如果eol為true,即檢測得到換行符,那么此時將緩存區(qū)中的數(shù)據(jù)裝換成String類型并返回
                if (eol) {
                    String str;
                //如果s為null,那么通過緩存區(qū)內(nèi)容新建一個String對象傳給str,否則調(diào)用append方法在s后追加內(nèi)容,然后通過toString方法返回String類型數(shù)據(jù)
            //給str。
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
            //讀取的位置向后移位,如果此時c為'\r',那么skipLF置為true。
                    nextChar++;
                    if (c == '\r') {
                        skipLF = true;
                    }
                    return str;
                }
                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                s.append(cb, startChar, i - startChar);
            }
        }
    }
 
    /**
     * 定義了一個readLine方法,每次讀取一行數(shù)據(jù)。本質(zhì)就是調(diào)用上面帶參的readLine方法,忽略換行符。
     */
    public String readLine() throws IOException {
        return readLine(false);
    }
 
    /**
     * 定義了一個帶參的skip方法,用于跳過指定參數(shù)個數(shù)的字符。傳入的參數(shù)是一個long型數(shù)據(jù)。
     */
    public long skip(long n) throws IOException {
    //對傳入的參數(shù)進(jìn)行安全監(jiān)測,如果n小于零,則拋出相應(yīng)異常。
        if (n < 0L) {
            throw new IllegalArgumentException("skip value is negative");
        }
        synchronized (lock) {
        //檢測流是否關(guān)閉。
            ensureOpen();
            long r = n;
            while (r > 0) {
        //如果當(dāng)前讀取位置超過緩存區(qū)容量,那么調(diào)用fill方法,向緩存中重新填充數(shù)據(jù)。
                if (nextChar >= nChars)
                    fill();
        //緩存區(qū)刷新后,如果讀取位置仍然大于等于緩存區(qū)中容量,那么此時跳出循環(huán)。
                if (nextChar >= nChars)
                    break;
        //通過skipLF來判讀是否需要對換行符進(jìn)行處理。
                if (skipLF) {
            //如果需要跳過換行符,那么當(dāng)遇到'\n'時,直接將讀取索引向后移位
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                    }
                }
        //定義了一個long型變量d,用于存放緩存區(qū)中剩余的字符數(shù)量。
                long d = nChars - nextChar;
        //如果需要跳過的字符數(shù)量小于等于d,那么直接將讀取索引向后移動r即可,然后將r置為0表示全部跳過。否則將r減去d,nextChar移動到緩存區(qū)尾部,
        //表示將緩存區(qū)中的內(nèi)容都跳過。
                if (r <= d) {
                    nextChar += r;
                    r = 0;
                    break;
                }
                else {
                    r -= d;
                    nextChar = nChars;
                }
            }
        //最終返回n-r,表示實(shí)際跳過的字符數(shù)。
            return n - r;
        }
    }
 
    /**
     * 定義了一個ready方法,用于判斷流中數(shù)據(jù)是否可讀。
     */
    public boolean ready() throws IOException {
        synchronized (lock) {
        //檢測當(dāng)前流是否關(guān)閉
            ensureOpen();
        //通過skipLF來判斷是否需要對換行符進(jìn)行處理。
            if (skipLF) {
        //如果當(dāng)前讀取位置超過了緩存區(qū)容量,且傳入的Reader對象是可讀的,那么調(diào)用fill方法,向緩存中填充數(shù)據(jù)。
                if (nextChar >= nChars && in.ready()) {
                    fill();
                }
        //緩存區(qū)刷新后,如果讀取位置在緩存區(qū)內(nèi)容范圍內(nèi),當(dāng)遇到換行符'\n'時,將讀取位置向后移動一位,最后重置skipLF狀態(tài)。
                if (nextChar < nChars) {
                    if (cb[nextChar] == '\n')
                        nextChar++;
                    skipLF = false;
                }
            }
        //最終通過當(dāng)前讀取位置是否在緩存區(qū)內(nèi)容范圍內(nèi)和傳入的Reader對象in是否可讀來決定當(dāng)前流是否可讀。
            return (nextChar < nChars) || in.ready();
        }
    }
 
    /**
     * 定一了一個markSupported方法,通過其返回值來判斷當(dāng)前流是否支持標(biāo)記功能,此處總是返回true,表示支持流標(biāo)記功能。
     */
    public boolean markSupported() {
        return true;
    }
 
    /**
     * 定義了一個帶參的mark方法,用于在流中當(dāng)前位置留下標(biāo)記,通過與reset方法聯(lián)合使用,可以在讀取過程中返回到標(biāo)記位置。傳入的參數(shù)為一個int型值,該值用于
     * 限定標(biāo)記允許的最大長度。
     */
    public void mark(int readAheadLimit) throws IOException {
    //對傳入的參數(shù)進(jìn)行安全監(jiān)測,如果readAheadLimit小于零,那么拋出相應(yīng)的異常。
        if (readAheadLimit < 0) {
            throw new IllegalArgumentException("Read-ahead limit < 0");
        }
        synchronized (lock) {
        //監(jiān)測當(dāng)前流是否關(guān)閉。
            ensureOpen();
        //將最初聲明的readAheadLimit賦值。
            this.readAheadLimit = readAheadLimit;
        //記錄下下一個要讀取的字符的位置。
            markedChar = nextChar;
        //記錄下是否需要忽略換行符。
            markedSkipLF = skipLF;
        }
    }
 
    /**
     * 定義了一個reset方法,通過與mark方法聯(lián)合使用,可以在讀取數(shù)據(jù)時返回到標(biāo)記處重新進(jìn)行數(shù)據(jù)讀取。
     */
    public void reset() throws IOException {
        synchronized (lock) {
        //檢測當(dāng)前流是否關(guān)閉。
            ensureOpen();
        //檢測markdChar,如果其小于0,表示當(dāng)前流中標(biāo)記功能無用。
            if (markedChar < 0)
                throw new IOException((markedChar == INVALIDATED)
                                      ? "Mark invalid"
                                      : "Stream not marked");
        //將下一個讀取位置置為標(biāo)記處的位置。將skipLF狀態(tài)置為保存的狀態(tài)。
            nextChar = markedChar;
            skipLF = markedSkipLF;
        }
    }
 
    /*
     * 定義了一個close方法用于關(guān)閉流,并將流中緩存區(qū)清除。
     */
    public void close() throws IOException {
        synchronized (lock) {
        //如果in已經(jīng)為null,則返回,否則調(diào)用其close方法,并將內(nèi)置的緩存區(qū)數(shù)組置為null。
            if (in == null)
                return;
            try {
                in.close();
            } finally {
                in = null;
                cb = null;
            }
        }
    }
 
    /**
     * 該方法是java1.8中的新特性,可以將I/O流轉(zhuǎn)換為字符串流方便我們對其進(jìn)行操作。
     */
    public Stream<String> lines() {
        Iterator<String> iter = new Iterator<String>() {
            String nextLine = null;
 
            @Override
            public boolean hasNext() {
                if (nextLine != null) {
                    return true;
                } else {
                    try {
                        nextLine = readLine();
                        return (nextLine != null);
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                }
            }
 
            @Override
            public String next() {
                if (nextLine != null || hasNext()) {
                    String line = nextLine;
                    nextLine = null;
                    return line;
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iter, Spliterator.ORDERED | Spliterator.NONNULL), false);
    }
}

BufferedWriter.java

package java.io;
 
public class BufferedWriter extends Writer {
    //內(nèi)置了一個Writer句柄,用于接收傳入的Writer對象,用于負(fù)責(zé)真正的磁盤之間的IO操作。
    private Writer out;
    //內(nèi)置了一個char型數(shù)組,用于當(dāng)做存儲緩存區(qū)。
    private char cb[];
    //聲明了兩個int型變量,nChars表示內(nèi)置緩存區(qū)數(shù)組的容量大小,nextChar表示下一個寫入的字符的位置下標(biāo)。
    private int nChars, nextChar;
    //聲明了一個靜態(tài)的int型變量defaultCharBufferSize,賦值8k,是內(nèi)置緩存區(qū)的默認(rèn)容量大小。
    private static int defaultCharBufferSize = 8192;
    //聲明了一個String類型的變量,用于接收運(yùn)行環(huán)境中的換行符(不同環(huán)境中的換行符不同)。
    private String lineSeparator;
 
    /**
     * 一個帶一個參數(shù)的構(gòu)造方法,傳入的參數(shù)為一個Writer型對象。內(nèi)部實(shí)質(zhì)是調(diào)用下面帶兩個參數(shù)的構(gòu)造方法。初始化時默認(rèn)緩存區(qū)大小為8k。
     */
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }
 
    /**
     * 一個帶兩個參數(shù)的構(gòu)造方法,第一個參數(shù)為一個Writer類型對象,將其賦值給開始聲明的Writer對象句柄,第二個參數(shù)是一個int型數(shù)值,它決定著初始化時的內(nèi)置緩
     * 區(qū)的容量大小。
     */
    public BufferedWriter(Writer out, int sz) {
    //調(diào)用父類的構(gòu)造函數(shù),將writer對象作為之后進(jìn)行同步操作的鎖對象。
        super(out);
    //如果傳入的int型變量<0,則拋出非法數(shù)據(jù)的異常。
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
    //將傳入的Wrter對象賦值給之前聲明的Writer對象句柄out,根據(jù)傳入的int型變量sz初始化內(nèi)置的緩存區(qū)數(shù)組cb,為nChars、nextChar賦上初始值。
        this.out = out;
        cb = new char[sz];
        nChars = sz;
        nextChar = 0;
    //根據(jù)當(dāng)前運(yùn)行環(huán)境,獲得換行符并賦值給最初定義的String型變量lineSeparator。
        lineSeparator = java.security.AccessController.doPrivileged(
            new sun.security.action.GetPropertyAction("line.separator"));
    }
 
    /** 
     * 定義了一個ensureOpen方法,見名知其義,用來判斷當(dāng)前流是否關(guān)閉,如果關(guān)閉了則拋出IO異常。
     */
    private void ensureOpen() throws IOException {
        if (out == null)
            throw new IOException("Stream closed");
    }
 
    /**
     * 定義了一個flushBuffer方法,用于將內(nèi)置緩存區(qū)的數(shù)據(jù)寫入到內(nèi)置的Writer里。
     */
    void flushBuffer() throws IOException {
        synchronized (lock) {
        //檢測Writer流是否關(guān)閉
            ensureOpen();
        //如果讀取位置為0,表示緩存區(qū)中沒有數(shù)據(jù),無需清空,所以直接return。
            if (nextChar == 0)
                return;
        //如果緩存區(qū)中有數(shù)據(jù),則將緩存區(qū)中數(shù)據(jù)寫出,將nextChar標(biāo)記位重置為0。
            out.write(cb, 0, nextChar);
            nextChar = 0;
        }
    }
 
    /**
     * 定義了一個帶一個參數(shù)的write方法,一次寫入一個字符。
     */
    public void write(int c) throws IOException {
        synchronized (lock) {
        //檢測流是否關(guān)閉
            ensureOpen();
        //如果內(nèi)置的緩存數(shù)組容量不足,調(diào)用flushBuffer方法,將緩存內(nèi)容寫出并清空。
            if (nextChar >= nChars)
                flushBuffer();
        //向緩存中寫入指定字符。
            cb[nextChar++] = (char) c;
        }
    }
 
    /**
     * 定義了一個帶兩個參數(shù)的min方法,用于返回兩個值之間的最小值。這里沒有使用java.lang.Math中的對應(yīng)方法,也許提升了運(yùn)行效率吧,畢竟少加載了一個類。
     */
    private int min(int a, int b) {
        if (a < b) return a;
        return b;
    }
 
    /**
     * 定義了一個帶三個參數(shù)的write方法,一次可以寫入多個字符。第一個字符為一個char型數(shù)組,用于存放需要寫入的數(shù)據(jù),第二個參數(shù)為一個int型數(shù)值,表示數(shù)組寫入
     * 的起始位置,第三個int型參數(shù)len表示從數(shù)組中寫入數(shù)據(jù)的長度。
     */
    public void write(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
        //檢測當(dāng)前流是否關(guān)閉。
            ensureOpen();
        //檢測傳入?yún)?shù)的準(zhǔn)確性。
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return;
            }
        //如果需要寫入的數(shù)據(jù)的長度大于了內(nèi)置緩存區(qū)的容量,那么先調(diào)用flushBuffer方法將緩存區(qū)中的數(shù)據(jù)先寫入,然后直接使用Writer寫入數(shù)據(jù),不使用緩存區(qū),
        //這樣就避免了先向緩存區(qū)中填充數(shù)據(jù)再從緩存區(qū)寫入這樣麻煩的步驟。
            if (len >= nChars) {
                flushBuffer();
                out.write(cbuf, off, len);
                return;
            }
 
        //定義了一個int型變量b表示當(dāng)前位置,一個int型變量t表示當(dāng)前位置寫入指定len長度后的位置。
            int b = off, t = off + len;
        //使用了一個循環(huán),用于寫入指定長度的數(shù)據(jù)。
            while (b < t) {
        //定義了一個int型變量d,比較緩存區(qū)數(shù)組剩余的容量和需要寫入的長度,將數(shù)值小的一方賦值給d。
                int d = min(nChars - nextChar, t - b);
        //將傳入cbuf中d長度的內(nèi)容填充到緩存區(qū)中。
                System.arraycopy(cbuf, b, cb, nextChar, d);
        //更新當(dāng)前位置
                b += d;
                nextChar += d;
        //如果當(dāng)前位置超過了內(nèi)置緩存容量,調(diào)用flushBuffer方法,將緩存內(nèi)容flush值Writer。
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }
 
    /**
     * 該方法用于直接寫入字符串形式的數(shù)據(jù)。
     */
    public void write(String s, int off, int len) throws IOException {
        synchronized (lock) {
        //檢測流是否關(guān)閉。
            ensureOpen();
 
            int b = off, t = off + len;
            while (b < t) {
                int d = min(nChars - nextChar, t - b);
                s.getChars(b, b + d, cb, nextChar);
                b += d;
                nextChar += d;
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }
 
    /**
     * 該方法用于向流中寫入一個換行符,起到換行的作用。
     */
    public void newLine() throws IOException {
        write(lineSeparator);
    }
 
    /**
     * 定義了一個flush方法,首先調(diào)用flushBuffer方法,將內(nèi)置的緩存flush到Writer中,然后調(diào)用Wrter的flush方法,將數(shù)據(jù)實(shí)際寫入至目的地。
     */
    public void flush() throws IOException {
        synchronized (lock) {
            flushBuffer();
            out.flush();
        }
    }
 
    /**
     * 定義了一個close方法,用于關(guān)閉流。
     */
    @SuppressWarnings("try")
    public void close() throws IOException {
        synchronized (lock) {
        //如果已經(jīng)關(guān)閉則無需其它操作
            if (out == null) {
                return;
            }
        //否則關(guān)閉流并清空緩存區(qū),最后將Writer對象句柄和緩存數(shù)組都指向null,讓jvm進(jìn)行資源回收。
            try (Writer w = out) {
                flushBuffer();
            } finally {
                out = null;
                cb = null;
            }
        }
    }
}

通過上面簡單的分析我們隊(duì)BufferedReader和BufferedWriter有了初步的認(rèn)識,下面通過一個簡單的例子來說明這兩個類的基本用法。由于筆者使用的不是jdk8,所以這里的新特性就沒有展示了。

package BufferedIOTest;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class BufferedIOTest2 {
    public static void main(String[] args) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(new File(
                "./src/file/test.txt")));
                BufferedWriter bw = new BufferedWriter(new FileWriter(new File(
                        "./src/file/testcopy2.txt")))) {
            String len;
            while ((len = br.readLine()) != null) {
                bw.write(len+(br.ready()?"\n":""));
            }
            System.out.println("copying file has been finished..");
        }
    }
}

執(zhí)行上述代碼,可以將制定位置的文件進(jìn)行拷貝。值得注意的是上面的例子在讀取時使用了BufferedReader的readLine方法,一次讀取一行數(shù)據(jù),在寫入數(shù)據(jù)的時候需要自己添加對應(yīng)的換行符。
以上為本篇的全部內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容