一、CharArrayReader的介绍
CharArrayReader继承自Reader,是Java字符输入流的一种实现,它在内部维护了一个字符缓冲区用于保存读取的字节数据,因此支持mark(标记)和reset(回滚)。
二、CharArrayReader的成员变量
package java.io;
public class CharArrayReader extends Reader {
/** 字符缓冲区 */
protected char buf[];
/**缓冲区数组buf下一个读取的字符的下标 */
protected int pos;
/** 缓冲区buf中标记的下标位置 **/
protected int markedPos = 0;
/** 缓冲区中保存的字符个数 **/
protected int count;
}
三、CharArrayReader源码分析
1 - 构造函数
/**
* 构造函数,指定字符缓冲区,初始化下一次读取位置pos和有效字符个数count
*/
public CharArrayReader(char buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
/**
* 构造函数,基于方法指定的字符数组buf,把数组起点offset作为读取的起点pos,数组长度-offset和length的较小值
* 设置为有效字符个数count
*/
public CharArrayReader(char buf[], int offset, int length) {
//数组开始读取起点offset和读取长度length范围校验
if ((offset < 0) || (offset > buf.length) || (length < 0) ||
((offset + length) < 0)) {
throw new IllegalArgumentException();
}
this.buf = buf;
this.pos = offset;
this.count = Math.min(offset + length, buf.length);
this.markedPos = offset;
}
通过对CharReader构造函数源码分析,可知CharReader字符读取的数据源保存在内部缓冲数组buf中,由CharReader实例化对象时构造函数的方法参数指定。第二个构造方法相对于第一个的区别在于它设置了读取的初始位置pos为方法指定参数offset
2 - 其他成员方法
本类的成员方法逻辑很简单,之前参考下述的源码注解理解。
/** 确认该字符输入流CharReader还未关闭,方法主要是基于对内部缓冲区buf判空实现的 **/
private void ensureOpen() throws IOException {
if (buf == null)
throw new IOException("Stream closed");
}
/**
* 读取一个字符
*/
public int read() throws IOException {
//加锁
synchronized (lock) {
//检测流是否已经关闭,即判断缓冲区buf是否为空
ensureOpen();
if (pos >= count)
return -1;
else
return buf[pos++];
}
}
/**
* 将CharReader保存在字符缓冲区中的字符数据读取到方法指定的字符数组,数组填充起点为offset,填入的字符个数由
* len和b.length-offset的最小值决定
*/
public int read(char b[], int off, int len) throws IOException {
synchronized (lock) {
//流是否关闭
ensureOpen();
//数组字符填充起点offset和数组长度len范围合法性校验
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {//读取长度为0直接结束返回
return 0;
}
//当前字符输入流字符数据已经全部读取直接返回-1
if (pos >= count) {
return -1;
}
//当前字符流剩余未读取的字符个数小于方法指定要读取的字节个数len,重新设置读取字节个数为剩余未读取字节个
//数即count-pos
if (pos + len > count) {
len = count - pos;
}
if (len <= 0) {
return 0;
}
//调用系统数组复制方法将缓冲区pos到pos+len范围的字符数据复制到方法指定字符数组b中,从该数组起点off开
//始填充字符数据
System.arraycopy(buf, pos, b, off, len);
//更新当前读取下标pos
pos += len;
return len;
}
}
/**
* 跳过指定个数字符,如果方法指定跳过字符数参数n小于0则不做任何处理
*/
public long skip(long n) throws IOException {
synchronized (lock) {
ensureOpen();
if (pos + n > count) {
n = count - pos;
}
if (n < 0) {
return 0;
}
pos += n;
return n;
}
}
/**
* 判断当前字符流是否已经准备好被读取,主要是基于缓冲数组buf和当前读取位置pos和可读字节数count判断
*/
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return (count - pos) > 0;
}
}
/**
* 判断CharReader是否支持mark()操作,返回true支持
*/
public boolean markSupported() {
return true;
}
/**
* 标记当前字符输入流的读取位置,后续调用reset方法会将流重新定位到该位置进行读取,方法指定参数readAheadLimit本
* 意是指定标记之后继续读取多少字符,流仍然保留该标记的字符读取限制,但因为CharReader数据源即内部缓冲区会保留所
* 有字符数据,所以该参数可以忽略
*/
public void mark(int readAheadLimit) throws IOException {
synchronized (lock) {
ensureOpen();
markedPos = pos;
}
}
/**
* 重置读取位置到上一次标记位置或者如果之前未曾标记则重置到流开头位置读取
*/
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
pos = markedPos;
}
}
/**
* 关闭流,实际就是把内部缓冲区数组置空
*/
public void close() {
buf = null;
}