BufferedWriter和BufferedReader与BufferedInputStream和BufferedOutputStream一样,是为了提高写入和读取的效率而设计的,它们都采用内部缓存数组的方式来提高read和write的效率,我们大致来看一下它们的实现。

BufferedWriter

BufferedWriter本质上也是Writer的一个子类:

  1. public class BufferedWriter extends Writer {}

与BufferedOutputStream内部有一个OutputStream类似,BufferedWriter内部也有一个writer来完成实际的写入:

  1. private Writer out;

然后还有一个缓存字符数组:

  1. private char cb[];
  2. private int nChars, nextChar;
  3. private static int defaultCharBufferSize = 8192;

defaultCharBufferSize是默认的缓存数组大小,当然,我们也可以通过构造方法来指定这个缓存数组的大小:

  1. public BufferedWriter(Writer out, int sz) {
  2. super(out);
  3. if (sz <= 0)
  4. throw new IllegalArgumentException("Buffer size <= 0");
  5. this.out = out;
  6. cb = new char[sz];
  7. nChars = sz;
  8. nextChar = 0;
  9. lineSeparator = java.security.AccessController.doPrivileged(
  10. new sun.security.action.GetPropertyAction("line.separator"));
  11. }

构造方法中初始化了内部的缓存字符数组。它还将nChars赋值为缓存长度大小,将nextChar设置为零。我们稍后就会看到这两个字段的用处。

单字符的write方法

  1. public void write(int c) throws IOException {
  2. synchronized (lock) {
  3. ensureOpen();
  4. if (nextChar >= nChars)
  5. flushBuffer();
  6. cb[nextChar++] = (char) c;
  7. }
  8. }

它先调用了ensureOpen方法:

  1. private void ensureOpen() throws IOException {
  2. if (out == null)
  3. throw new IOException("Stream closed");
  4. }

其实就是判断内部的writer是否为null。
然后判断nextChar是否大于等于nChars,我们先来看这个条件不满足的情况,执行:

  1. cb[nextChar++] = (char) c;

其实就是将这个字符写到缓存字符数组nextChar的位置,然后将nextChar加一,所以nextChar应该代表的应该是缓存字符数组中下一个空白的位置。这样的逻辑,就实现了如果缓冲区未满,就将字符直接存入缓冲区的逻辑。
那就是nextChar>=nChars是缓冲区已满的情况,所以nChars应该就是当前缓冲区的大小,也就是缓冲字符数组的length。那么flushBuffer就是刷新缓冲区的操作了:

  1. void flushBuffer() throws IOException {
  2. synchronized (lock) {
  3. ensureOpen();
  4. if (nextChar == 0)
  5. return;
  6. out.write(cb, 0, nextChar);
  7. nextChar = 0;
  8. }
  9. }

调用内部的writer的write方法将缓冲字符数组中的字符全部写入,然后将nextChar置零,这样缓冲区就清空了。

多字节的write方法

  1. public void write(char cbuf[], int off, int len) throws IOException {
  2. synchronized (lock) {
  3. ensureOpen();
  4. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
  5. ((off + len) > cbuf.length) || ((off + len) < 0)) {
  6. throw new IndexOutOfBoundsException();
  7. } else if (len == 0) {
  8. return;
  9. }
  10. if (len >= nChars) {
  11. /* If the request length exceeds the size of the output buffer,
  12. flush the buffer and then write the data directly. In this
  13. way buffered streams will cascade harmlessly. */
  14. flushBuffer();
  15. out.write(cbuf, off, len);
  16. return;
  17. }
  18. int b = off, t = off + len;
  19. while (b < t) {
  20. int d = min(nChars - nextChar, t - b);
  21. System.arraycopy(cbuf, b, cb, nextChar, d);
  22. b += d;
  23. nextChar += d;
  24. if (nextChar >= nChars)
  25. flushBuffer();
  26. }
  27. }
  28. }

这个方法稍微复杂,有一点值得注意,如果要写入的字符的数量大于缓冲字符数组的长度,会直接清空缓存然后将所有的数据直接通过内部的writer的write写入。

BufferedReader