JDK源码-IO系列:字符流输入FileReader

字符流输入 FileReader

读FileReader的时候,结合BuffedReader一起看。参考前一篇文章FileReader

先说结论

BufferedReader 封装了FileReader和字符缓冲区这两个关键内容。
BufferedReader通过FileReader从文件中获取字节流并decode解码成字符数组,存储再字符缓冲去。
BufferedReader通过字符缓冲区给调用方获取指定位置的字符。

类图

在这里插入图片描述
FileReader继承了InputStreamReader。
InputStreamReader的filed sd是StreamDecoder。
StreamDecoder有几类属性:
1、用于存储从in InputStream或者ch ReadableByteChannel读取的字节数组的bb 字节缓冲区
2、decoder,用于解码字节数组的解码器。decoder是CharsetDeoder实力,因为字符编解码有很多,包括UTF_8。类图以UTF_8为例讲解。当编码方式选择UTF_8的时候,decoder就是一个UTF_8编码器实例;

关键方法

FileReader.read()

FileReader.java

public int read() throws IOException {
	   //代理了一下StreamDecoder.reader()
        return sd.read();
    }

StreamDecoder.java

    public int read() throws IOException {
        return read0();
    }

    @SuppressWarnings("fallthrough")
    private int read0() throws IOException {
        synchronized (lock) {

            // Return the leftover char, if there is one
            if (haveLeftoverChar) {
                haveLeftoverChar = false;
                return leftoverChar;
            }

            // Convert more bytes
            //cb是一个长度为2的字符数组,当第一次调用read0的时候,读取的是cb的第一个字符,当第二次调用read0方法的时候
            //读取的是第二个字符,而读取第一/第二个字符就是靠haveLeftoverChar和leftoverChar实现的。
            //读取第一个字符后,havaleftoverChar变为true,leftoverChar存储cb[1],也就是存储第二个字符。
            char cb[] = new char[2];
            int n = read(cb, 0, 2);
            switch (n) {
            case -1:
                return -1;
            case 2:
                leftoverChar = cb[1];
                haveLeftoverChar = true;
                // FALL THROUGH
            case 1:
                return cb[0];
            default:
                assert false : n;
                return -1;
            }
        }
    }

注意看StreamDecoder.java中的read0方法。
cb是一个长度为2的字符数组,当第一次调用read0的时候,读取的是cb的第一个字符,当第二次调用read0方法的时候
读取的是第二个字符,而读取第一/第二个字符就是靠haveLeftoverChar和leftoverChar实现的。
读取第一个字符后,havaleftoverChar变为true,leftoverChar存储cb[1],也就是存储第二个字符。

总结一下:
使用FileReader的逐字符读取的时候,实际上FileReader相当于2个字符的缓冲区数组不断读取,读取完成后又调用decoder对bb字节数组进行字符解码。解码详细解读参考FileReader讲解。

FileReader.read(char cbuf[], int offset, int length)

FileReader.java

    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }
    

StreamDecoder.java

     public int read(char cbuf[], int offset, int length) throws IOException {
        int off = offset;
        int len = length;
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            }
            if (len == 0)
                return 0;

            int n = 0;

            if (haveLeftoverChar) {
                // Copy the leftover char into the buffer
                cbuf[off] = leftoverChar;
                off++; len--;
                haveLeftoverChar = false;
                n = 1;
                if ((len == 0) || !implReady())
                    // Return now if this is all we can produce w/o blocking
                    return n;
            }

            if (len == 1) {
                // Treat single-character array reads just like read()
                int c = read0();
                if (c == -1)
                    return (n == 0) ? -1 : n;
                cbuf[off] = (char)c;
                return n + 1;
            }

            return n + implRead(cbuf, off, off + len);
        }
    }

可以看到,当len=1的时候,就相当于read()方法,逐字读取。
当len不是1的时候,执行implRead(cbuf, off, off+len)方法。这个方法实际上就是decode解码字节缓冲区bb,将解码到的字符存储在cbuf数组中。

比较一下FileReader和BufferedReader

本质上都是通过StreamDecoder对文件进行一次io,读取8192个字节存储到bb字节缓冲区,之后通过解码器进行CPU计算出来要取的指定个数的字符。
而BufferedReader使用了默认长度为8192的字符缓冲区来存储解码后的字符数据。
假如一个文件中有8192个中文字符“你”,我们知道一个“你”,用过UTF_8编码,需要3个字节。
那么如果通过BufferedReader读取完所有字符。有这么几个过程:
1、StreamDecoder一次文件io,读取8192个字节到bb缓冲区,之后通过UTF_8解码器将字节解码为字符,可以解码2730个字符。然后将2730个字符存储到字符缓冲区,而因为剩下的2个字节不足以完成一个字符的解码,所以保留最后这两个字节。
2、StreamDecoder第二次文件io,读取8190个字节,加上第一步剩下的的2个字节,总共还是8192个字节,继续进行解码。
不断往复累计进行4次,即可完成BufferedReader的8192个字符的缓冲区的填充。

如果使用FileReader的效果是什么?
如果调用FileReader的read(char cbuf[], int offset, int length) 方法,offset=0,length=8192;那么实际上和上面BufferedReader的效果是类似的。包括文件交互的io数。
但是如果是逐字符读取,那每次都要调用解码器解码的次数多很多。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值