从JDK源码看Reader

概况

Reader 是一个用于读字符流的抽象类,它将一些相通的读相关操作抽象到此类,方便各种读操作类的实现。一般来说子类只需要实现它的 read 和 close 两个方法,但如果有需要还可以重写 Reader 提供的公共方法。

JDK 在 Reader 的基础上实现了很多有用的 xxxReader ,包括 BufferedReader、CharArrayReader、FilterReader、InputStreamReader、FileReader、PipedReader、StringReader 和 LineNumberReader 等等。

继承结构

--java.lang.Object
  --java.io.Reader
复制代码

类定义

public abstract class Reader implements Readable, Closeable
复制代码

Reader 被定为 public 且 abstract 的类,实现了 Readable 和 Closeable接口。

Readable 接口表示尝试将字符读取到指定的缓冲中,接口定义如下:

public interface Readable {
    public int read(java.nio.CharBuffer cb) throws IOException;
}
复制代码

Closeable 接口表示 Reader 可以被close,接口定义如下:

public interface Closeable extends AutoCloseable {
    public void close() throws IOException;
}
复制代码

主要属性

private static final int maxSkipBufferSize = 8192;

private char skipBuffer[] = null;

protected Object lock;
复制代码
  • maxSkipBufferSize 最大跳过缓冲的大小。
  • skipBuffer 是一个 char[] 类型,表示跳过缓冲。
  • lock 是 Reader 的锁,用于实现同步。

构造方法

有两种构造方法,不带参数时则将自己作为锁,而如果传入了某个 Object 对象则将其作为锁。

protected Reader() {
    this.lock = this;
}
protected Reader(Object lock) {
    if (lock == null) {
        throw new NullPointerException();
    }
    this.lock = lock;
}
复制代码

主要方法

read方法

一共有四个 read 方法,其中有一个抽象的 read 方法,可以看到所有 read 方法最终都会调用这个抽象方法,提供给子类处理逻辑的实现。它传入的三个参数,字符数组cbuf、偏移量off和数组长度。

public abstract int read(char cbuf[], int off, int len) throws IOException;
复制代码

无参的 read 方法其实是默认读一个字符,new 一个 char 对象然后调用子类实现进行读取,最后返回读到的字符。

public int read() throws IOException {
    char cb[] = new char[1];
    if (read(cb, 0, 1) == -1)
        return -1;
    else
        return cb[0];
}
    
复制代码

假如 read 方法传入的参数为 char 数组时,则直接调用子类实现进行读取。

public int read(char cbuf[]) throws IOException {
    return read(cbuf, 0, cbuf.length);
}
复制代码

最后一个 read 方法其实是 Readable 接口定义的方法,用于读取字符到指定的 CharBuffer 对象中,逻辑是先得到 CharBuffer 对象剩余长度,根据该长度实例化 char 数组,然后调用子类实现完成读取,最后将读取到的字符放进 CharBuffer 对象。

public int read(java.nio.CharBuffer target) throws IOException {
    int len = target.remaining();
    char[] cbuf = new char[len];
    int n = read(cbuf, 0, len);
    if (n > 0)
        target.put(cbuf, 0, n);
    return n;
}
复制代码

ready方法

表示该读取器是否已准备好,默认返回 false,如果能保证调用 read 方法读取下一个字符不阻塞则返回 true。

public boolean ready() throws IOException {
    return false;
}
复制代码

skip方法

该方法用于跳过指定长度字符,期间如果某些字符还未可读则可能发生阻塞,另外期间如果发生 IO 错误则会抛异常。逻辑是,

  1. 跳过字符长度不能小于0。
  2. 跳过长度不能超过最大跳过长度 maxSkipBufferSize,超过则只能取 maxSkipBufferSize。
  3. 加锁开始处理,skipBuffer 对象如果为空则需要先实例化指定长度的 char 数组。
  4. 不断循环调用子类的 read 方法进行读取,对读取到的字符不做处理,即实现了跳过效果。
public long skip(long n) throws IOException {
    if (n < 0L)
        throw new IllegalArgumentException("skip value is negative");
    int nn = (int) Math.min(n, maxSkipBufferSize);
    synchronized (lock) {
        if ((skipBuffer == null) || (skipBuffer.length < nn))
            skipBuffer = new char[nn];
        long r = n;
        while (r > 0) {
            int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
            if (nc == -1)
                break;
            r -= nc;
        }
        return n - r;
    }
}
复制代码

close方法

它是一个抽象方法,留给子类实现。此方法用于关闭流,并且释放相关的资源 。关闭后再调用 read()、ready()、mark()、reset()或skip()等方法将抛出 IO 异常。

public abstract void close() throws IOException;
复制代码

markSupported方法

是否支持 mark 和 reset 操作,这里直接返回 false,子类根据实际重写该方法。

public boolean markSupported() {
    return false;
}
复制代码

mark方法

标记读取器当前的位置,与之对应的是 reset 方法,通过他们之间的组合能实现重复读取操作。默认是不支持此操作的,需要子类重写该方法。

public void mark(int readAheadLimit) throws IOException {
    throw new IOException("mark() not supported");
}
复制代码

reset方法

与 mark 方法对应,它可以重置读取器的位置到上次被 mark 操作标识的位置,如果未被标记过则可能会被重置到开始的位置。默认是不支持此操作的,需要子类重写该方法。

public void reset() throws IOException {
    throw new IOException("reset() not supported");
}
复制代码

=============广告时间===============

公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

欢迎关注:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值