(最近剛來到簡書平臺,以前在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)容。