概况
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 错误则会抛异常。逻辑是,
- 跳过字符长度不能小于0。
- 跳过长度不能超过最大跳过长度 maxSkipBufferSize,超过则只能取 maxSkipBufferSize。
- 加锁开始处理,skipBuffer 对象如果为空则需要先实例化指定长度的 char 数组。
- 不断循环调用子类的 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");
}
复制代码