2021-05-04 19:36  阅读(1737)
文章分类:从 JDK 源码看 Java 文章标签:JavaJDK 源码
©  原文作者:超人汪小建(seaboat) 原文地址:https://blog.csdn.net/wangyangzhizhou/column/info/16032

概况

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");
        }
    
点赞(1)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 从JDK源码看Reader
上一篇
从JDK源码看OutputStream
下一篇
细看Java序列化机制