Java 中的 BufferedReader 介紹_動力節(jié)點Java學(xué)院整理
BufferedReader 介紹
BufferedReader 是緩沖字符輸入流。它繼承于Reader。
BufferedReader 的作用是為其他字符輸入流添加一些緩沖功能。
BufferedReader 函數(shù)列表
BufferedReader(Reader in) BufferedReader(Reader in, int size) void close() void mark(int markLimit) boolean markSupported() int read() int read(char[] buffer, int offset, int length) String readLine() boolean ready() void reset() long skip(long charCount)
BufferedReader 源碼分析(基于jdk1.7.40)
package java.io;
public class BufferedReader extends Reader {
private Reader in;
// 字符緩沖區(qū)
private char cb[];
// nChars 是cb緩沖區(qū)中字符的總的個數(shù)
// nextChar 是下一個要讀取的字符在cb緩沖區(qū)中的位置
private int nChars, nextChar;
// 表示“標記無效”。它與UNMARKED的區(qū)別是:
// (01) UNMARKED 是壓根就沒有設(shè)置過標記。
// (02) 而INVALIDATED是設(shè)置了標記,但是被標記位置太長,導(dǎo)致標記無效!
private static final int INVALIDATED = -2;
// 表示沒有設(shè)置“標記”
private static final int UNMARKED = -1;
// “標記”
private int markedChar = UNMARKED;
// “標記”能標記位置的最大長度
private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
// skipLF(即skip Line Feed)是“是否忽略換行符”標記
private boolean skipLF = false;
// 設(shè)置“標記”時,保存的skipLF的值
private boolean markedSkipLF = false;
// 默認字符緩沖區(qū)大小
private static int defaultCharBufferSize = 8192;
// 默認每一行的字符個數(shù)
private static int defaultExpectedLineLength = 80;
// 創(chuàng)建“Reader”對應(yīng)的BufferedReader對象,sz是BufferedReader的緩沖區(qū)大小
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;
}
// 創(chuàng)建“Reader”對應(yīng)的BufferedReader對象,默認的BufferedReader緩沖區(qū)大小是8k
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}
// 確?!癇ufferedReader”是打開狀態(tài)
private void ensureOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
}
// 填充緩沖區(qū)函數(shù)。有以下兩種情況被調(diào)用:
// (01) 緩沖區(qū)沒有數(shù)據(jù)時,通過fill()可以向緩沖區(qū)填充數(shù)據(jù)。
// (02) 緩沖區(qū)數(shù)據(jù)被讀完,需更新時,通過fill()可以更新緩沖區(qū)的數(shù)據(jù)。
private void fill() throws IOException {
// dst表示“cb中填充數(shù)據(jù)的起始位置”。
int dst;
if (markedChar <= UNMARKED) {
// 沒有標記的情況,則設(shè)dst=0。
dst = 0;
} else {
// delta表示“當前標記的長度”,它等于“下一個被讀取字符的位置”減去“標記的位置”的差值;
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
// 若“當前標記的長度”超過了“標記上限(readAheadLimit)”,
// 則丟棄標記!
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (readAheadLimit <= cb.length) {
// 若“當前標記的長度”沒有超過了“標記上限(readAheadLimit)”,
// 并且“標記上限(readAheadLimit)”小于/等于“緩沖的長度”;
// 則先將“下一個要被讀取的位置,距離我們標記的置符的距離”間的字符保存到cb中。
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
// 若“當前標記的長度”沒有超過了“標記上限(readAheadLimit)”,
// 并且“標記上限(readAheadLimit)”大于“緩沖的長度”;
// 則重新設(shè)置緩沖區(qū)大小,并將“下一個要被讀取的位置,距離我們標記的置符的距離”間的字符保存到cb中。
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
// 更新nextChar和nChars
nextChar = nChars = delta;
}
}
int n;
do {
// 從“in”中讀取數(shù)據(jù),并存儲到字符數(shù)組cb中;
// 從cb的dst位置開始存儲,讀取的字符個數(shù)是cb.length - dst
// n是實際讀取的字符個數(shù);若n==0(即一個也沒讀到),則繼續(xù)讀取!
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
// 如果從“in”中讀到了數(shù)據(jù),則設(shè)置nChars(cb中字符的數(shù)目)=dst+n,
// 并且nextChar(下一個被讀取的字符的位置)=dst。
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
// 從BufferedReader中讀取一個字符,該字符以int的方式返回
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
for (;;) {
// 若“緩沖區(qū)的數(shù)據(jù)已經(jīng)被讀完”,
// 則先通過fill()更新緩沖區(qū)數(shù)據(jù)
if (nextChar >= nChars) {
fill();
if (nextChar >= nChars)
return -1;
}
// 若要“忽略換行符”,
// 則對下一個字符是否是換行符進行處理。
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
continue;
}
}
// 返回下一個字符
return cb[nextChar++];
}
}
}
// 將緩沖區(qū)中的數(shù)據(jù)寫入到數(shù)組cbuf中。off是數(shù)組cbuf中的寫入起始位置,len是寫入長度
private int read(char[] cbuf, int off, int len) throws IOException {
// 若“緩沖區(qū)的數(shù)據(jù)已經(jīng)被讀完”,則更新緩沖區(qū)數(shù)據(jù)。
if (nextChar >= nChars) {
if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
return in.read(cbuf, off, len);
}
fill();
}
// 若更新數(shù)據(jù)之后,沒有任何變化;則退出。
if (nextChar >= nChars) return -;
// 若要“忽略換行符”,則進行相應(yīng)處理
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
if (nextChar >= nChars)
fill();
if (nextChar >= nChars)
return -1;
}
}
// 拷貝字符操作
int n = Math.min(len, nChars - nextChar);
System.arraycopy(cb, nextChar, cbuf, off, n);
nextChar += n;
return n;
}
// 對read()的封裝,添加了“同步處理”和“阻塞式讀取”等功能
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = read1(cbuf, off, len);
if (n <= 0) return n;
while ((n < len) && in.ready()) {
int n1 = read1(cbuf, off + n, len - n);
if (n1 <= 0) break;
n += n1;
}
return n;
}
}
// 讀取一行數(shù)據(jù)。ignoreLF是“是否忽略換行符”
String readLine(boolean ignoreLF) throws IOException {
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)
return s.toString();
else
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;
if (s == null) {
str = new String(cb, startChar, i - startChar);
} else {
s.append(cb, startChar, i - startChar);
str = s.toString();
}
nextChar++;
if (c == '\r') {
skipLF = true;
}
return str;
}
if (s == null)
s = new StringBuffer(defaultExpectedLineLength);
s.append(cb, startChar, i - startChar);
}
}
}
// 讀取一行數(shù)據(jù)。不忽略換行符
public String readLine() throws IOException {
return readLine(false);
}
// 跳過n個字符
public long skip(long n) throws IOException {
if (n < 0L) {
throw new IllegalArgumentException("skip value is negative");
}
synchronized (lock) {
ensureOpen();
long r = n;
while (r > 0) {
if (nextChar >= nChars)
fill();
if (nextChar >= nChars) /* EOF */
break;
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
}
}
long d = nChars - nextChar;
if (r <= d) {
nextChar += r;
r = 0;
break;
}
else {
r -= d;
nextChar = nChars;
}
}
return n - r;
}
}
// “下一個字符”是否可讀
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
// 若忽略換行符為true;
// 則判斷下一個符號是否是換行符,若是的話,則忽略
if (skipLF) {
if (nextChar >= nChars && in.ready()) {
fill();
}
if (nextChar < nChars) {
if (cb[nextChar] == '\n')
nextChar++;
skipLF = false;
}
}
return (nextChar < nChars) || in.ready();
}
}
// 始終返回true。因為BufferedReader支持mark(), reset()
public boolean markSupported() {
return true;
}
// 標記當前BufferedReader的下一個要讀取位置。關(guān)于readAheadLimit的作用,參考后面的說明。
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0) {
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
// 設(shè)置readAheadLimit
this.readAheadLimit = readAheadLimit;
// 保存下一個要讀取的位置
markedChar = nextChar;
// 保存“是否忽略換行符”標記
markedSkipLF = skipLF;
}
}
// 重置BufferedReader的下一個要讀取位置,
// 將其還原到mark()中所保存的位置。
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
if (markedChar < 0)
throw new IOException((markedChar == INVALIDATED)
? "Mark invalid"
: "Stream not marked");
nextChar = markedChar;
skipLF = markedSkipLF;
}
}
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
in.close();
in = null;
cb = null;
}
}
}
說明:
要想讀懂BufferReader的源碼,就要先理解它的思想。BufferReader的作用是為其它Reader提供緩沖功能。創(chuàng)建BufferReader時,我們會通過它的構(gòu)造函數(shù)指定某個Reader為參數(shù)。BufferReader會將該Reader中的數(shù)據(jù)分批讀取,每次讀取一部分到緩沖中;操作完緩沖中的這部分數(shù)據(jù)之后,再從Reader中讀取下一部分的數(shù)據(jù)。
為什么需要緩沖呢?原因很簡單,效率問題!緩沖中的數(shù)據(jù)實際上是保存在內(nèi)存中,而原始數(shù)據(jù)可能是保存在硬盤或NandFlash中;而我們知道,從內(nèi)存中讀取數(shù)據(jù)的速度比從硬盤讀取數(shù)據(jù)的速度至少快10倍以上。
那干嘛不干脆一次性將全部數(shù)據(jù)都讀取到緩沖中呢?第一,讀取全部的數(shù)據(jù)所需要的時間可能會很長。第二,內(nèi)存價格很貴,容量不想硬盤那么大。
下面,我就BufferReader中最重要的函數(shù)fill()進行說明。其它的函數(shù)很容易理解,我就不詳細介紹了,大家可以參考源碼中的注釋進行理解。我們先看看fill()的源碼:
private void fill() throws IOException {
int dst;
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
} else {
/* Marked */
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
/* Gone past read-ahead limit: Invalidate mark */
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (readAheadLimit <= cb.length) {
/* Shuffle in the current buffer */
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
/* Reallocate buffer to accommodate read-ahead limit */
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
nextChar = nChars = delta;
}
}
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
根據(jù)fill()中的if...else...,我將fill()分為4種情況進行說明。
情況1:讀取完緩沖區(qū)的數(shù)據(jù),并且緩沖區(qū)沒有被標記
執(zhí)行流程如下,
(01) 其它函數(shù)調(diào)用 fill(),來更新緩沖區(qū)的數(shù)據(jù)
(02) fill() 執(zhí)行代碼 if (markedChar <= UNMARKED) { ... }
為了方便分析,我們將這種情況下fill()執(zhí)行的操作等價于以下代碼:
private void fill() throws IOException {
int dst;
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
}
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
說明:
這種情況發(fā)生的情況是 — — Reader中有很長的數(shù)據(jù),我們每次從中讀取一部分數(shù)據(jù)到緩沖中進行操作。每次當我們讀取完緩沖中的數(shù)據(jù)之后,并且此時BufferedReader沒有被標記;那么,就接著從Reader(BufferReader提供緩沖功能的Reader)中讀取下一部分的數(shù)據(jù)到緩沖中。
其中,判斷是否讀完緩沖區(qū)中的數(shù)據(jù),是通過“比較nextChar和nChars之間大小”來判斷的。其中,nChars 是緩沖區(qū)中字符的總的個數(shù),而 nextChar 是緩沖區(qū)中下一個要讀取的字符的位置。
判斷BufferedReader有沒有被標記,是通過“markedChar”來判斷的。
理解這個思想之后,我們再對這種情況下的fill()的代碼進行分析,就特別容易理解了。
(01) if (markedChar <= UNMARKED) 它的作用是判斷“BufferedReader是否被標記”。若被標記,則dst=0。
(02) in.read(cb, dst, cb.length - dst) 等價于 in.read(cb, 0, cb.length),意思是從Reader對象in中讀取cb.length個數(shù)據(jù),并存儲到緩沖區(qū)cb中,而且從緩沖區(qū)cb的位置0開始存儲。該函數(shù)返回值等于n,也就是n表示實際讀取的字符個數(shù)。若n=0(即沒有讀取到數(shù)據(jù)),則繼續(xù)讀取,直到讀到數(shù)據(jù)為止。
(03) nChars=dst+n 等價于 nChars=n;意味著,更新緩沖區(qū)數(shù)據(jù)cb之后,設(shè)置nChars(緩沖區(qū)的數(shù)據(jù)個數(shù))為n。
(04) nextChar=dst 等價于 nextChar=0;意味著,更新緩沖區(qū)數(shù)據(jù)cb之后,設(shè)置nextChar(緩沖區(qū)中下一個會被讀取的字符的索引值)為0。
情況2:讀取完緩沖區(qū)的數(shù)據(jù),緩沖區(qū)的標記位置>0,并且“當前標記的長度”超過“標記上限(readAheadLimit)”
執(zhí)行流程如下,
(01) 其它函數(shù)調(diào)用 fill(),來更新緩沖區(qū)的數(shù)據(jù)
(02) fill() 執(zhí)行代碼 if (delta >= readAheadLimit) { ... }
為了方便分析,我們將這種情況下fill()執(zhí)行的操作等價于以下代碼:
private void fill() throws IOException {
int dst;
if (markedChar > UNMARKED) {
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
}
}
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
說明:
這種情況發(fā)生的情況是 — — BufferedReader中有很長的數(shù)據(jù),我們每次從中讀取一部分數(shù)據(jù)到緩沖區(qū)中進行操作。當我們讀取完緩沖區(qū)中的數(shù)據(jù)之后,并且此時,BufferedReader存在標記時,同時,“當前標記的長度”大于“標記上限”;那么,就發(fā)生情況2。此時,我們會丟棄“標記”并更新緩沖區(qū)。
(01) delta = nextChar - markedChar;其中,delta就是“當前標記的長度”,它是“下一個被讀取字符的位置”減去“被標記的位置”的差值。
(02) if (delta >= readAheadLimit);其中,當delta >= readAheadLimit,就意味著,“當前標記的長度”>=“標記上限”。為什么要有標記上限,即readAheadLimit的值到底有何意義呢?
我們標記一個位置之后,更新緩沖區(qū)的時候,被標記的位置會被保存;當我們不停的更新緩沖區(qū)的時候,被標記的位置會被不停的放大。然后內(nèi)存的容量是有效的,我們不可能不限制長度的存儲標記。所以,需要readAheadLimit來限制標記長度!
(03) in.read(cb, dst, cb.length - dst) 等價于 in.read(cb, 0, cb.length),意思是從Reader對象in中讀取cb.length個數(shù)據(jù),并存儲到緩沖區(qū)cb中,而且從緩沖區(qū)cb的位置0開始存儲。該函數(shù)返回值等于n,也就是n表示實際讀取的字符個數(shù)。若n=0(即沒有讀取到數(shù)據(jù)),則繼續(xù)讀取,直到讀到數(shù)據(jù)為止。
(04) nChars=dst+n 等價于 nChars=n;意味著,更新緩沖區(qū)數(shù)據(jù)cb之后,設(shè)置nChars(緩沖區(qū)的數(shù)據(jù)個數(shù))為n。
(05) nextChar=dst 等價于 nextChar=0;意味著,更新緩沖區(qū)數(shù)據(jù)cb之后,設(shè)置nextChar(緩沖區(qū)中下一個會被讀取的字符的索引值)為0。
情況3:讀取完緩沖區(qū)的數(shù)據(jù),緩沖區(qū)的標記位置>0,“當前標記的長度”沒超過“標記上限(readAheadLimit)”,并且“標記上限(readAheadLimit)”小于/等于“緩沖的長度”;
執(zhí)行流程如下,
(01) 其它函數(shù)調(diào)用 fill(),來更新緩沖區(qū)的數(shù)據(jù)
(02) fill() 執(zhí)行代碼 if (readAheadLimit <= cb.length) { ... }
為了方便分析,我們將這種情況下fill()執(zhí)行的操作等價于以下代碼:
private void fill() throws IOException {
int dst;
if (markedChar > UNMARKED) {
int delta = nextChar - markedChar;
if ((delta < readAheadLimit) && (readAheadLimit <= cb.length) ) {
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
nextChar = nChars = delta;
}
}
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
說明:
這種情況發(fā)生的情況是 — — BufferedReader中有很長的數(shù)據(jù),我們每次從中讀取一部分數(shù)據(jù)到緩沖區(qū)中進行操作。當我們讀取完緩沖區(qū)中的數(shù)據(jù)之后,并且此時,BufferedReader存在標記時,同時,“當前標記的長度”小于“標記上限”,并且“標記上限”小于/等于“緩沖區(qū)長度”;那么,就發(fā)生情況3。此時,我們保留“被標記的位置”(即,保留被標記位置開始的數(shù)據(jù)),并更新緩沖區(qū)(將新增的數(shù)據(jù),追加到保留的數(shù)據(jù)之后)。
情況4:讀取完緩沖區(qū)的數(shù)據(jù),緩沖區(qū)的標記位置>0,“當前標記的長度”沒超過“標記上限(readAheadLimit)”,并且“標記上限(readAheadLimit)”大于“緩沖的長度”;
執(zhí)行流程如下,
(01) 其它函數(shù)調(diào)用 fill(),來更新緩沖區(qū)的數(shù)據(jù)
(02) fill() 執(zhí)行代碼 else { char ncb[] = new char[readAheadLimit]; ... }
為了方便分析,我們將這種情況下fill()執(zhí)行的操作等價于以下代碼:
private void fill() throws IOException {
int dst;
if (markedChar > UNMARKED) {
int delta = nextChar - markedChar;
if ((delta < readAheadLimit) && (readAheadLimit > cb.length) ) {
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
nextChar = nChars = delta;
}
}
int n;
do {
n = in.read(cb, dst, cb.length - dst);
} while (n == );
if (n > ) {
nChars = dst + n;
nextChar = dst;
}
}
說明:
這種情況發(fā)生的情況是 — — BufferedReader中有很長的數(shù)據(jù),我們每次從中讀取一部分數(shù)據(jù)到緩沖區(qū)中進行操作。當我們讀取完緩沖區(qū)中的數(shù)據(jù)之后,并且此時,BufferedReader存在標記時,同時,“當前標記的長度”小于“標記上限”,并且“標記上限”大于“緩沖區(qū)長度”;那么,就發(fā)生情況4。此時,我們要先更新緩沖區(qū)的大小,然后再保留“被標記的位置”(即,保留被標記位置開始的數(shù)據(jù)),并更新緩沖區(qū)數(shù)據(jù)(將新增的數(shù)據(jù),追加到保留的數(shù)據(jù)之后)。
示例代碼
關(guān)于BufferedReader中API的詳細用法,參考示例代碼(BufferedReaderTest.java):
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.lang.SecurityException;
/**
* BufferedReader 測試程序
*
*
*/
public class BufferedReaderTest {
private static final int LEN = 5;
public static void main(String[] args) {
testBufferedReader() ;
}
/**
* BufferedReader的API測試函數(shù)
*/
private static void testBufferedReader() {
// 創(chuàng)建BufferedReader字符流,內(nèi)容是ArrayLetters數(shù)組
try {
File file = new File("bufferedreader.txt");
BufferedReader in =
new BufferedReader(
new FileReader(file));
// 從字符流中讀取5個字符?!癮bcde”
for (int i=0; i<LEN; i++) {
// 若能繼續(xù)讀取下一個字符,則讀取下一個字符
if (in.ready()) {
// 讀取“字符流的下一個字符”
int tmp = in.read();
System.out.printf("%d : %c\n", i, tmp);
}
}
// 若“該字符流”不支持標記功能,則直接退出
if (!in.markSupported()) {
System.out.println("make not supported!");
return ;
}
// 標記“當前索引位置”,即標記第6個位置的元素--“f”
// 1024對應(yīng)marklimit
in.mark(1024);
// 跳過22個字符。
in.skip(22);
// 讀取5個字符
char[] buf = new char[LEN];
in.read(buf, 0, LEN);
System.out.printf("buf=%s\n", String.valueOf(buf));
// 讀取該行剩余的數(shù)據(jù)
System.out.printf("readLine=%s\n", in.readLine());
// 重置“輸入流的索引”為mark()所標記的位置,即重置到“f”處。
in.reset();
// 從“重置后的字符流”中讀取5個字符到buf中。即讀取“fghij”
in.read(buf, , LEN);
System.out.printf("buf=%s\n", String.valueOf(buf));
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序中讀取的bufferedreader.txt的內(nèi)容如下:
abcdefghijklmnopqrstuvwxyz 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ
運行結(jié)果:
0 : a 1 : b 2 : c 3 : d 4 : e buf=01234 readLine=56789 buf=fghij
以上所述是小編給大家介紹的Java 中的 BufferedReader 介紹_動力節(jié)點Java學(xué)院整理,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Java基礎(chǔ)類庫之StringBuffer類用法詳解
String類是在所有開發(fā)項目開發(fā)之中一定會使用的一個功能類。雖然String類很好用,但也有弊端——內(nèi)容不允許頻繁修改,所以為了解決問題,我們提供了StringBuffer類。本文就來講講StringBuffer類的用法2022-07-07
JPA之EntityManager踩坑及解決:更改PersistenceContext
這篇文章主要介紹了JPA之EntityManager踩坑及解決:更改PersistenceContext方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
IDEA創(chuàng)建Maven工程Servlet的詳細教程
這篇文章主要介紹了IDEA創(chuàng)建Maven工程Servlet的詳細教程,本文給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
RestTemplate設(shè)置超時時間及返回狀態(tài)碼非200處理
這篇文章主要為大家介紹了RestTemplate設(shè)置超時時間及返回狀態(tài)碼非200處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
MybatisPlus?BaseMapper?實現(xiàn)對數(shù)據(jù)庫增刪改查源碼
MybatisPlus?是一款在?Mybatis?基礎(chǔ)上進行的增強?orm?框架,可以實現(xiàn)不寫?sql?就完成數(shù)據(jù)庫相關(guān)的操作,這篇文章主要介紹了MybatisPlus?BaseMapper?實現(xiàn)對數(shù)據(jù)庫增刪改查源碼解析,需要的朋友可以參考下2023-01-01
在CentOS上安裝Java 17并實現(xiàn)多版本共存的詳細教程
在現(xiàn)代軟件開發(fā)中,Java 作為一種廣泛使用的編程語言,其版本更新頻繁,不同項目可能依賴不同版本的 Java 運行環(huán)境,CentOS 作為一款流行的 Linux 發(fā)行版,常被用于服務(wù)器部署和開發(fā)環(huán)境,本文將詳細介紹如何在 CentOS 上安裝 Java 17,并實現(xiàn)與現(xiàn)有 Java 8 的多版本共存2025-03-03
Java 中組合模型之對象結(jié)構(gòu)模式的詳解
這篇文章主要介紹了Java 中組合模型之對象結(jié)構(gòu)模式的詳解的相關(guān)資料,希望通過本文能幫助到大家理解應(yīng)用對象結(jié)構(gòu)模型,需要的朋友可以參考下2017-09-09
MyBatis-Plus UpdateWrapper 使用常見陷阱和解決方案
MyBatis-Plus是Mybatis的一個增強,簡化了Mybatis的開發(fā)過程,不僅保持了Mybatis原有的功能,而且在無代碼侵略下增加了許多的增強的功能,提供了豐富的CRUD操作,單表的CRUD操作無需編寫SQL語句,本文介紹的是UpdateWrapper的常見陷阱和對應(yīng)的解決方案,感興趣的朋友一起看看吧2024-08-08

