(最近剛來到簡書平臺(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)容早歇。