本篇讲述的是java io包中的BufferedReader和BufferedWriter。从名字中可以看出它们分别是Reader和Writer的子类,它们的特点是在对流进行读写操作时,内置了缓存区,通过减少与磁盘之间IO操作的此处,从而提升了读写效率,下面我们来简要的看看它们的源码。
BufferedReader.java:
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 {
//内置了一个Reader对象句柄in,用于接收传入的Reader对象。
private Reader in;
//定义了一个char类型的数组,作为内置的数据存储区,默认大小为8k。
private char cb[];
//声明了两个int型变量nChars和nextChar,nChars表示缓存区中存在的字符数据的个数,nextChar表示下一个要读取的字符内容在缓存区中所在的位置。
private int nChars, nextChar;
//定义了两个常量值分别代表了流内标记的两种状态,INVALIDATED表示流中曾经有过标记,但是超过了标记的限制长度,标记失效,UNMARKED则是没有做过标记。
private static final int INVALIDATED = -2;
private static final int UNMARKED = -1;
//声明了一个int型变量markedChar,该变量表示了当前流中的标记状态,初始化默认值为UNMARKED。
private int markedChar = UNMARKED;
//声明了一个int型变量readAheadLimit,该值是流中标记的限制长度,如果标记位置超过了该值,则标记会被抛弃。该值只有大于零时,才起作用。
private int readAheadLimit = 0;
//声明了一个boolean型变量skipLF,该值用于表示是否忽略换行标记,初始化默认值为false,表示不忽略。
private boolean skipLF = false;
//声明了一个boolean型变量markedSkipLF,该值用于保存skipLF的状态。
private boolean markedSkipLF = false;
//定义了一个int型的变量值defaultCharBufferSize,该值表示内置缓存区中的容量大小,初始化默认值为8k。
private static int defaultCharBufferSize = 8192;
//定义了一个int型的变量值defaultExpectedLineLength,该值表示了每行中字符数的长度,初始化默认值为80。
private static int defaultExpectedLineLength = 80;
/**
* 定义了带两个参数的构造函数,第一个参数为一个Reader型对象,用于给内部定义的Reader对象句柄赋值,第二个参数为一个int型参数sz,用来初始化内部缓存区的
* 容量。
*/
public BufferedReader(Reader in, int sz) {
//调用父类Reader中的构造方法,将传入的Reader对象作为锁对象,为后面的同步操作提供同步锁。
super(in);
//对传入的参数进行安全检测,如果sz小于0则抛出对应异常。
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
//将传入的Reader对象,赋值给内部声明的Reader对象句柄。
this.in = in;
//初始化内置的缓存数组cb,通过传入的参数sz来确定cb的容量大小。
cb = new char[sz];
初始化nextChar,nChars的值,刚开始都为0。
nextChar = nChars = 0;
}
/**
* 定义了一个带一个参数的构造函数,传入的参数类型为一个Reader对象,内部实际是调用上面的构造方法,内置缓存区使用默认大小创建,容量为8k。
*/
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}
/**
* 该方法用于检测当前流是否关闭。如果已经关闭,则抛出相应的异常。
*/
private void ensureOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
}
/**
* 该方法是整个类中的核心方法,用于向内置的缓存区中填充数据。
*/
private void fill() throws IOException {
//定义了一个int型值dst,用来表示内置缓存区中有效数据的起始位置。
int dst;
//如果markedChar <= UNMARKED,则表示当前流中没有标记,因此可以大胆地直接将dst重置为0,后面会对缓存区进行重新填充。
if (markedChar <= UNMARKED) {
dst = 0;
} else {
//以下情况表示流中存在标记情况。//定义了一个int型值delta,用于接收即将读取的下一个字符位置于流中标记位置的差值。
int delta = nextChar - markedChar;
//如果delta>=readAheadLimit,表示当前标记的位置已经超过了标记长度的限制,那么此时直接将标记清空。
if (delta >= readAheadLimit) {
//将流标记状态恢复为INVALIDATED,表示因为超过标记长度限制,流标记失效。
markedChar = INVALIDATED;
//因为流标记失效,所以此时流标记的长度限制也归零。
readAheadLimit = 0;
//将内部缓存区的有效数据的起始位置归零。
dst = 0;
} else {
//如果标记的限制长度readAheadLimit小于等于内置缓存区的容量,那么此时将内置缓存区中从标记处的数据复制到缓存数组的开头,同时将标记处置为0,
//此时缓存区中有效数据长度为delta。
if (readAheadLimit <= cb.length) {
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
//如果标记的限制长度readAheadLimit大于内置缓存区的容量,那么直接扩容缓存区数组,将其容量直接扩展到标记的限制值大小,将原先的内置缓存
//区中自标记处开始的内容复制到扩容的新数组处,并将扩容后的数组赋值给内置的缓存数组cb。此时标记处重置为0,缓存区中有效长度为delta。
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
//此时即将读取的下一个字符的索引和数组缓存区中的有效数字都等于delta的值。
nextChar = nChars = delta;
}
}
//通过一个循环,向内置的缓存区中填充