BufferedWriter和BufferedReader与BufferedInputStream和BufferedOutputStream一样,是为了提高写入和读取的效率而设计的,它们都采用内部缓存数组的方式来提高read和write的效率,我们大致来看一下它们的实现。
BufferedWriter
BufferedWriter本质上也是Writer的一个子类:
public class BufferedWriter extends Writer {}
与BufferedOutputStream内部有一个OutputStream类似,BufferedWriter内部也有一个writer来完成实际的写入:
private Writer out;
然后还有一个缓存字符数组:
private char cb[];
private int nChars, nextChar;
private static int defaultCharBufferSize = 8192;
defaultCharBufferSize是默认的缓存数组大小,当然,我们也可以通过构造方法来指定这个缓存数组的大小:
public BufferedWriter(Writer out, int sz) {
super(out);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.out = out;
cb = new char[sz];
nChars = sz;
nextChar = 0;
lineSeparator = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("line.separator"));
}
构造方法中初始化了内部的缓存字符数组。它还将nChars赋值为缓存长度大小,将nextChar设置为零。我们稍后就会看到这两个字段的用处。
单字符的write方法
public void write(int c) throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar >= nChars)
flushBuffer();
cb[nextChar++] = (char) c;
}
}
它先调用了ensureOpen方法:
private void ensureOpen() throws IOException {
if (out == null)
throw new IOException("Stream closed");
}
其实就是判断内部的writer是否为null。
然后判断nextChar是否大于等于nChars,我们先来看这个条件不满足的情况,执行:
cb[nextChar++] = (char) c;
其实就是将这个字符写到缓存字符数组nextChar的位置,然后将nextChar加一,所以nextChar应该代表的应该是缓存字符数组中下一个空白的位置。这样的逻辑,就实现了如果缓冲区未满,就将字符直接存入缓冲区的逻辑。
那就是nextChar>=nChars是缓冲区已满的情况,所以nChars应该就是当前缓冲区的大小,也就是缓冲字符数组的length。那么flushBuffer就是刷新缓冲区的操作了:
void flushBuffer() throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar == 0)
return;
out.write(cb, 0, nextChar);
nextChar = 0;
}
}
调用内部的writer的write方法将缓冲字符数组中的字符全部写入,然后将nextChar置零,这样缓冲区就清空了。
多字节的write方法
public void write(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;
}
if (len >= nChars) {
/* If the request length exceeds the size of the output buffer,
flush the buffer and then write the data directly. In this
way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(cbuf, off, len);
return;
}
int b = off, t = off + len;
while (b < t) {
int d = min(nChars - nextChar, t - b);
System.arraycopy(cbuf, b, cb, nextChar, d);
b += d;
nextChar += d;
if (nextChar >= nChars)
flushBuffer();
}
}
}
这个方法稍微复杂,有一点值得注意,如果要写入的字符的数量大于缓冲字符数组的长度,会直接清空缓存然后将所有的数据直接通过内部的writer的write写入。