Java IO筆記(BufferedReader/BufferedWriter)


(最近剛來到簡書平臺(tái)假抄,以前在CSDN上寫的一些東西近上,也在逐漸的移到這兒來,有些篇幅是很早的時(shí)候?qū)懴碌牟癜穑虼丝赡軙?huì)看到一些內(nèi)容雜亂的文章陨溅,對此深感抱歉,以下為正文)


正文

本篇講述的是Java IO包中的BufferedReader和BufferedWriter绍在。從名字中可以看出它們分別是Reader和Writer的子類门扇,它們的特點(diǎn)是在對流進(jìn)行讀寫操作時(shí),內(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)置了一個(gè)Reader對象句柄in吉拳,用于接收傳入的Reader對象。
    private Reader in;
    //定義了一個(gè)char類型的數(shù)組适揉,作為內(nèi)置的數(shù)據(jù)存儲(chǔ)區(qū)留攒,默認(rèn)大小為8k煤惩。
    private char cb[];
    //聲明了兩個(gè)int型變量nChars和nextChar,nChars表示緩存區(qū)中存在的字符數(shù)據(jù)的個(gè)數(shù)炼邀,nextChar表示下一個(gè)要讀取的字符內(nèi)容在緩存區(qū)中所在的位置魄揉。
    private int nChars, nextChar;
    //定義了兩個(gè)常量值分別代表了流內(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;
    //聲明了一個(gè)int型變量markedChar兵怯,該變量表示了當(dāng)前流中的標(biāo)記狀態(tài),初始化默認(rèn)值為UNMARKED在旱。
    private int markedChar = UNMARKED;
    //聲明了一個(gè)int型變量readAheadLimit摇零,該值是流中標(biāo)記的限制長度,如果標(biāo)記位置超過了該值桶蝎,則標(biāo)記會(huì)被拋棄。該值只有大于零時(shí)谅畅,才起作用登渣。
    private int readAheadLimit = 0;
 
    //聲明了一個(gè)boolean型變量skipLF,該值用于表示是否忽略換行標(biāo)記毡泻,初始化默認(rèn)值為false胜茧,表示不忽略。
    private boolean skipLF = false;
    //聲明了一個(gè)boolean型變量markedSkipLF仇味,該值用于保存skipLF的狀態(tài)呻顽。
    private boolean markedSkipLF = false;
    //定義了一個(gè)int型的變量值defaultCharBufferSize,該值表示內(nèi)置緩存區(qū)中的容量大小丹墨,初始化默認(rèn)值為8k廊遍。
    private static int defaultCharBufferSize = 8192;
    //定義了一個(gè)int型的變量值defaultExpectedLineLength,該值表示了每行中字符數(shù)的長度贩挣,初始化默認(rèn)值為80喉前。
    private static int defaultExpectedLineLength = 80;
 
    /**
     * 定義了帶兩個(gè)參數(shù)的構(gòu)造函數(shù),第一個(gè)參數(shù)為一個(gè)Reader型對象王财,用于給內(nèi)部定義的Reader對象句柄賦值卵迂,第二個(gè)參數(shù)為一個(gè)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;
    }
 
    /**
     * 定義了一個(gè)帶一個(gè)參數(shù)的構(gòu)造函數(shù)盾鳞,傳入的參數(shù)類型為一個(gè)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");
    }
 
    /**
     * 該方法是整個(gè)類中的核心方法,用于向內(nèi)置的緩存區(qū)中填充數(shù)據(jù)验辞。
     */
    private void fill() throws IOException {
    //定義了一個(gè)int型值dst稿黄,用來表示內(nèi)置緩存區(qū)中有效數(shù)據(jù)的起始位置。
        int dst;
    //如果markedChar <= UNMARKED跌造,則表示當(dāng)前流中沒有標(biāo)記杆怕,因此可以大膽地直接將dst重置為0,后面會(huì)對緩存區(qū)進(jìn)行重新填充壳贪。
        if (markedChar <= UNMARKED) {
            dst = 0;
        } else {
            //以下情況表示流中存在標(biāo)記情況陵珍。//定義了一個(gè)int型值delta,用于接收即將讀取的下一個(gè)字符位置于流中標(biāo)記位置的差值违施。
            int delta = nextChar - markedChar;
        //如果delta>=readAheadLimit互纯,表示當(dāng)前標(biāo)記的位置已經(jīng)超過了標(biāo)記長度的限制,那么此時(shí)直接將標(biāo)記清空醉拓。
            if (delta >= readAheadLimit) {
        //將流標(biāo)記狀態(tài)恢復(fù)為INVALIDATED伟姐,表示因?yàn)槌^標(biāo)記長度限制,流標(biāo)記失效亿卤。
                markedChar = INVALIDATED;
        //因?yàn)榱鳂?biāo)記失效愤兵,所以此時(shí)流標(biāo)記的長度限制也歸零。
                readAheadLimit = 0;
        //將內(nèi)部緩存區(qū)的有效數(shù)據(jù)的起始位置歸零排吴。
                dst = 0;
            } else {
        //如果標(biāo)記的限制長度readAheadLimit小于等于內(nèi)置緩存區(qū)的容量秆乳,那么此時(shí)將內(nèi)置緩存區(qū)中從標(biāo)記處的數(shù)據(jù)復(fù)制到緩存數(shù)組的開頭,同時(shí)將標(biāo)記處置為0,
        //此時(shí)緩存區(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。此時(shí)標(biāo)記處重置為0荣刑,緩存區(qū)中有效長度為delta馅笙。
                    char ncb[] = new char[readAheadLimit];
                    System.arraycopy(cb, markedChar, ncb, 0, delta);
                    cb = ncb;
                    markedChar = 0;
                    dst = delta;
                }
        //此時(shí)即將讀取的下一個(gè)字符的索引和數(shù)組緩存區(qū)中的有效數(shù)字都等于delta的值。 
                nextChar = nChars = delta;
            }
        }
    
    //通過一個(gè)循環(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;
        }
    }
 
    /**
     * 定義了一個(gè)read方法皿淋,每次讀取一個(gè)字符。事實(shí)上是對read1的一個(gè)封裝恬试,添加了同步和阻塞等功能窝趣。
     */
    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ù)已讀完,此時(shí)需要調(diào)用fill方法重新向緩存區(qū)中填充數(shù)據(jù)畦粮。
                if (nextChar >= nChars) {
                    fill();
            //如果緩存區(qū)中更新后,nextChar還是大于等于nChars乖阵,那么表示此時(shí)文件已讀到末尾宣赔,返回-1。
                    if (nextChar >= nChars)
                        return -1;
                }
        //根據(jù)skipLF的值瞪浸,判斷是否對換行符進(jìn)行處理儒将,如果為true,則跳過換行符对蒲。
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                        continue;
                    }
                }
                return cb[nextChar++];
            }
        }
    }
 
    /**
     * 定義了一個(gè)帶三個(gè)參數(shù)的read方法钩蚊,一次可以讀取多個(gè)字符數(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)記并且對換行符不做處理的時(shí)候,那么直接調(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)讀取完畢,此時(shí)返回-1菊霜。
        if (nextChar >= nChars) return -1;
    //根據(jù)skipLF的值坚冀,判斷是否對換行符進(jìn)行處理,如果為true鉴逞,則進(jìn)行相應(yīng)處理记某。
        if (skipLF) {
            skipLF = false;
        //如果下一個(gè)讀取的字符為換行符,那么跳過华蜒,并檢測下一次讀取是否超過緩存區(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;
            }
        }
    //定義了一個(gè)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;
    }
 
    /**
     * 定義了帶一個(gè)參數(shù)的readLine方法,該方法一次讀取一行數(shù)據(jù)级乍,傳入的參數(shù)是一個(gè)boolean型數(shù)值舌劳,表示是否忽略換行符,該方法最終返回讀取到的字符串玫荣。
     */
    String readLine(boolean ignoreLF) throws IOException {
    //聲明了一個(gè)StringBuffer對象甚淡,用于存儲(chǔ)讀取到的字符數(shù)據(jù)。
        StringBuffer s = null;
        //聲明了一個(gè)int型變量startChar捅厂,表示數(shù)據(jù)讀取的起始位置贯卦。
    int startChar;
 
        synchronized (lock) {
        //檢測流是否關(guān)閉
            ensureOpen();
        //定義了一個(gè)boolean型變量omitLF,用于判斷是否負(fù)略換行符焙贷,通過對ignoreLF和skipLF兩個(gè)值進(jìn)行或操作來得出是否需要忽略換行符撵割。
            boolean omitLF = ignoreLF || skipLF;
    
    //定義了一個(gè)循環(huán)來讀取數(shù)據(jù),在循環(huán)外部定義了一個(gè)標(biāo)簽bufferLoop盈厘,方便跳出循環(huán)睁枕。
        bufferLoop:
            for (;;) {
        //如果nextChar >= nChars,則表示讀取的位置超過了數(shù)組緩存區(qū)中容量,那么此時(shí)需要向內(nèi)置緩存區(qū)中重新填充數(shù)據(jù)外遇。
                if (nextChar >= nChars)
                    fill();
        //數(shù)據(jù)填充結(jié)束后再次對讀取位置進(jìn)行檢測注簿,如果nextChar仍然大于等于nChars,那么表示文件已經(jīng)讀取完畢跳仿。
                if (nextChar >= nChars) {
            //如果次蘇滬杭存儲(chǔ)內(nèi)容的s中有內(nèi)容诡渴,則將s轉(zhuǎn)化為字符串并返回,否則返回null菲语。
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
        //定義了一個(gè)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)中嵌套了一個(gè)循環(huán)用于尋找換行符佩憾,并添加了一個(gè)標(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妄帘,即檢測得到換行符楞黄,那么此時(shí)將緩存區(qū)中的數(shù)據(jù)裝換成String類型并返回
                if (eol) {
                    String str;
                //如果s為null,那么通過緩存區(qū)內(nèi)容新建一個(gè)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();
                    }
            //讀取的位置向后移位致盟,如果此時(shí)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);
            }
        }
    }
 
    /**
     * 定義了一個(gè)readLine方法馏锡,每次讀取一行數(shù)據(jù)蚣录。本質(zhì)就是調(diào)用上面帶參的readLine方法,忽略換行符眷篇。
     */
    public String readLine() throws IOException {
        return readLine(false);
    }
 
    /**
     * 定義了一個(gè)帶參的skip方法,用于跳過指定參數(shù)個(gè)數(shù)的字符荔泳。傳入的參數(shù)是一個(gè)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ū)中容量叹侄,那么此時(shí)跳出循環(huán)巩搏。
                if (nextChar >= nChars)
                    break;
        //通過skipLF來判讀是否需要對換行符進(jìn)行處理。
                if (skipLF) {
            //如果需要跳過換行符趾代,那么當(dāng)遇到'\n'時(shí)贯底,直接將讀取索引向后移位
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                    }
                }
        //定義了一個(gè)long型變量d,用于存放緩存區(qū)中剩余的字符數(shù)量撒强。
                long d = nChars - nextChar;
        //如果需要跳過的字符數(shù)量小于等于d禽捆,那么直接將讀取索引向后移動(dòng)r即可,然后將r置為0表示全部跳過飘哨。否則將r減去d胚想,nextChar移動(dòng)到緩存區(qū)尾部,
        //表示將緩存區(qū)中的內(nèi)容都跳過芽隆。
                if (r <= d) {
                    nextChar += r;
                    r = 0;
                    break;
                }
                else {
                    r -= d;
                    nextChar = nChars;
                }
            }
        //最終返回n-r浊服,表示實(shí)際跳過的字符數(shù)。
            return n - r;
        }
    }
 
    /**
     * 定義了一個(gè)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'時(shí)代虾,將讀取位置向后移動(dòng)一位进肯,最后重置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();
        }
    }
 
    /**
     * 定一了一個(gè)markSupported方法江掩,通過其返回值來判斷當(dāng)前流是否支持標(biāo)記功能,此處總是返回true乘瓤,表示支持流標(biāo)記功能环形。
     */
    public boolean markSupported() {
        return true;
    }
 
    /**
     * 定義了一個(gè)帶參的mark方法,用于在流中當(dāng)前位置留下標(biāo)記衙傀,通過與reset方法聯(lián)合使用抬吟,可以在讀取過程中返回到標(biāo)記位置。傳入的參數(shù)為一個(gè)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;
        //記錄下下一個(gè)要讀取的字符的位置刃鳄。
            markedChar = nextChar;
        //記錄下是否需要忽略換行符盅弛。
            markedSkipLF = skipLF;
        }
    }
 
    /**
     * 定義了一個(gè)reset方法,通過與mark方法聯(lián)合使用叔锐,可以在讀取數(shù)據(jù)時(shí)返回到標(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");
        //將下一個(gè)讀取位置置為標(biāo)記處的位置步责。將skipLF狀態(tài)置為保存的狀態(tài)返顺。
            nextChar = markedChar;
            skipLF = markedSkipLF;
        }
    }
 
    /*
     * 定義了一個(gè)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)置了一個(gè)Writer句柄舟陆,用于接收傳入的Writer對象,用于負(fù)責(zé)真正的磁盤之間的IO操作耻矮。
    private Writer out;
    //內(nèi)置了一個(gè)char型數(shù)組秦躯,用于當(dāng)做存儲(chǔ)緩存區(qū)。
    private char cb[];
    //聲明了兩個(gè)int型變量裆装,nChars表示內(nèi)置緩存區(qū)數(shù)組的容量大小踱承,nextChar表示下一個(gè)寫入的字符的位置下標(biāo)。
    private int nChars, nextChar;
    //聲明了一個(gè)靜態(tài)的int型變量defaultCharBufferSize哨免,賦值8k勾扭,是內(nèi)置緩存區(qū)的默認(rèn)容量大小。
    private static int defaultCharBufferSize = 8192;
    //聲明了一個(gè)String類型的變量铁瞒,用于接收運(yùn)行環(huán)境中的換行符(不同環(huán)境中的換行符不同)。
    private String lineSeparator;
 
    /**
     * 一個(gè)帶一個(gè)參數(shù)的構(gòu)造方法桅滋,傳入的參數(shù)為一個(gè)Writer型對象慧耍。內(nèi)部實(shí)質(zhì)是調(diào)用下面帶兩個(gè)參數(shù)的構(gòu)造方法身辨。初始化時(shí)默認(rèn)緩存區(qū)大小為8k。
     */
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }
 
    /**
     * 一個(gè)帶兩個(gè)參數(shù)的構(gòu)造方法芍碧,第一個(gè)參數(shù)為一個(gè)Writer類型對象煌珊,將其賦值給開始聲明的Writer對象句柄,第二個(gè)參數(shù)是一個(gè)int型數(shù)值泌豆,它決定著初始化時(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"));
    }
 
    /** 
     * 定義了一個(gè)ensureOpen方法俱病,見名知其義,用來判斷當(dāng)前流是否關(guān)閉袱结,如果關(guān)閉了則拋出IO異常亮隙。
     */
    private void ensureOpen() throws IOException {
        if (out == null)
            throw new IOException("Stream closed");
    }
 
    /**
     * 定義了一個(gè)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;
        }
    }
 
    /**
     * 定義了一個(gè)帶一個(gè)參數(shù)的write方法,一次寫入一個(gè)字符欣硼。
     */
    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;
        }
    }
 
    /**
     * 定義了一個(gè)帶兩個(gè)參數(shù)的min方法,用于返回兩個(gè)值之間的最小值焦匈。這里沒有使用java.lang.Math中的對應(yīng)方法血公,也許提升了運(yùn)行效率吧,畢竟少加載了一個(gè)類缓熟。
     */
    private int min(int a, int b) {
        if (a < b) return a;
        return b;
    }
 
    /**
     * 定義了一個(gè)帶三個(gè)參數(shù)的write方法累魔,一次可以寫入多個(gè)字符摔笤。第一個(gè)字符為一個(gè)char型數(shù)組,用于存放需要寫入的數(shù)據(jù)垦写,第二個(gè)參數(shù)為一個(gè)int型數(shù)值吕世,表示數(shù)組寫入
     * 的起始位置,第三個(gè)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;
            }
 
        //定義了一個(gè)int型變量b表示當(dāng)前位置蹬敲,一個(gè)int型變量t表示當(dāng)前位置寫入指定len長度后的位置赋荆。
            int b = off, t = off + len;
        //使用了一個(gè)循環(huán)午磁,用于寫入指定長度的數(shù)據(jù)尝抖。
            while (b < t) {
        //定義了一個(gè)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();
            }
        }
    }
 
    /**
     * 該方法用于向流中寫入一個(gè)換行符,起到換行的作用。
     */
    public void newLine() throws IOException {
        write(lineSeparator);
    }
 
    /**
     * 定義了一個(gè)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();
        }
    }
 
    /**
     * 定義了一個(gè)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)識(shí)衣厘,下面通過一個(gè)簡單的例子來說明這兩個(gè)類的基本用法。由于筆者使用的不是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)行拷貝。值得注意的是上面的例子在讀取時(shí)使用了BufferedReader的readLine方法坤检,一次讀取一行數(shù)據(jù),在寫入數(shù)據(jù)的時(shí)候需要自己添加對應(yīng)的換行符期吓。
以上為本篇的全部內(nèi)容早歇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市讨勤,隨后出現(xiàn)的幾起案子箭跳,更是在濱河造成了極大的恐慌,老刑警劉巖潭千,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谱姓,死亡現(xiàn)場離奇詭異,居然都是意外死亡刨晴,警方通過查閱死者的電腦和手機(jī)屉来,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狈癞,“玉大人茄靠,你說我怎么就攤上這事〉埃” “怎么了慨绳?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長真竖。 經(jīng)常有香客問我脐雪,道長,這世上最難降的妖魔是什么恢共? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任战秋,我火速辦了婚禮,結(jié)果婚禮上旁振,老公的妹妹穿的比我還像新娘获询。我一直安慰自己,他們只是感情好拐袜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布吉嚣。 她就那樣靜靜地躺著,像睡著了一般蹬铺。 火紅的嫁衣襯著肌膚如雪尝哆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天甜攀,我揣著相機(jī)與錄音秋泄,去河邊找鬼琐馆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恒序,可吹牛的內(nèi)容都是我干的瘦麸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼歧胁,長吁一口氣:“原來是場噩夢啊……” “哼滋饲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起喊巍,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤屠缭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后崭参,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呵曹,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年何暮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奄喂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡郭卫,死狀恐怖砍聊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贰军,我是刑警寧澤玻蝌,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站词疼,受9級(jí)特大地震影響俯树,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贰盗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一许饿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧舵盈,春花似錦陋率、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赴蝇,卻和暖如春菩浙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工劲蜻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陆淀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓先嬉,卻偏偏與公主長得像轧苫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子疫蔓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容