Java I/O 18 - CharArrayReader & CharArrayWriter

  关于 java.io.CharArrayReader java.io.CharArrayWriter 的部分笔记,这两个类属于内存流的一种,参考流的思想操作内存空间中的数组内容。本文演示代码段的执行环境基于JDK版本1.7

概述

  CharArrayReader和CharArrayWriter是典型的内存流,这两个类在内存中维护了一个char数组,把char数组视为输入/输出流的位置并实现了一套对char数组进行读写的方法API。可以在一些需要Reader/Writer作为参数,且实际内容是char数组的场合中使用CharArrayReader和CharArrayWriter。因为没有调用过除了内存之外的其他系统资源,所以这两个类的close()方法除了释放char数组占用的资源外无其他操作。

继承关系

1
2
3
4
5
6
7
8
9
// CharArrayReader
--java.lang.Object
--java.io.Reader
--java.io.CharArrayReader

// CharArrayWriter
--java.lang.Object
--java.io.Writer
--java.io.CharArrayWriter

实现接口

类名 实现接口
CharArrayReader Closeable, AutoCloseable,Readable
CharArrayWriter Closeable, Flushable, AutoCloseable, Appendable

CharArrayReader

Constructor Summary

public CharArrayReader(char buf[])

1
2
3
4
5
public CharArrayReader(char buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}

  初始化一个字符数组输入流。该输入流的数据来源是字符数组buf,同时初始化pos值和数据长度count。

public CharArrayReader(char buf[], int offset, int length)

1
2
3
4
5
6
7
8
9
10
public CharArrayReader(char buf[], int offset, int 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;
}

  初始化一个字符数组输入流。第2 ~ 5行代码用来完成参数的有效性校验。第8行代码用来计算实际的数组结束边界。第9行代码用来标识数据重读的起始边界,因为offset可能不为0,也就意味着offset之前的内容永远不会读到,通过设置markedPos可以保证这一点不会发生。

部分方法

private void ensureOpen()

1
2
3
4
private void ensureOpen() throws IOException {
if (buf == null)
throw new IOException("Stream closed");
}

  检查当前buf是否有效且是否含有内容。

public int read()

1
2
3
4
5
6
7
8
9
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (pos >= count)
return -1;
else
return buf[pos++];
}
}

  从buf中读取一个字符的内容并返回。通过synchronized关键字保证了多线程环境下的线程安全。如果pos到达了数组边界,则认为数组内容已经全部读取完毕,返回EOF标识给方法调用方。

public int read(char b[], int off, int len)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public int read(char b[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}

if (pos >= count) {
return -1;
}
if (pos + len > count) {
len = count - pos;
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
}

  从buf中读取长度为len的内容并保存到数组b中起始位置为off的空间中。通过synchronized关键字保证了多线程环境下的线程安全。第4 ~ 9行代码完成了参数的有效性校验。如果pos到达了数组边界,则认为数组内容已经全部读取完毕,返回EOF标识给方法调用方。第14 ~ 16行代码计算实际可以读取的长度值。

  第20行代码则将buf中自pos位置起长度为len的内容复制到数组b中起始位置为off的空间里。之后计算更新后的pos值并返回实际读取的内容长度。

public long skip(long n)

1
2
3
4
5
6
7
8
9
10
11
12
13
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中数组中指定长度的内容。通过synchronized关键字保证了多线程环境下的线程安全。第4 ~ 6行代码计算实际需要跳过的长度。如果入参n是一个负数,那么什么操作都不执行,直接返回0。第10行代码计算跳过后的pos值,并返回实际跳过的长度值给方法调用方。

public boolean ready()

1
2
3
4
5
6
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return (count - pos) > 0;
}
}

  通知方法调用方当前输入流是否可以通过read方法提供字符内容。通过synchronized关键字保证了多线程环境下的线程安全。如果count - pos > 0 则认为当前底层buf中还有未读取的内容可供read方法读取获得。

public boolean markSupported()

1
2
3
public boolean markSupported() {
return true;
}

  CharArrayReader支持标记重读操作,所以返回true。

public void mark(int readAheadLimit)

1
2
3
4
5
6
public void mark(int readAheadLimit) throws IOException {
synchronized (lock) {
ensureOpen();
markedPos = pos;
}
}

  标记重读起始位置。当前方法调用后如果执行reset()方法,下一个读取位置pos值会变成当前执行mark方法时的pos值,实现重读功能。

public void reset()

1
2
3
4
5
6
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
pos = markedPos;
}
}

  重置下一个读取位置值。如果在调用reset()方法之前从未调用过mark()方法,那么调用reset()方法后会将pos重置为0,即从头读取。

public void close()

1
2
3
public void close() {
buf = null;
}

  关闭当前输入流。

CharArrayWriter

Constructor Summary

public CharArrayWriter()

1
2
3
public CharArrayWriter() {
this(32);
}

  初始化一个字符数组输出流,底层字符数组的长度为32。

public CharArrayWriter(int initialSize)

1
2
3
4
5
6
7
public CharArrayWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative initial size: "
+ initialSize);
}
buf = new char[initialSize];
}

  初始化一个字符数组输出流,底层字符数组的长度为入参initialSize。因为在初始化时需要依赖initialSize,所以该字段的值不能小于0。

部分方法

public void writeTo(Writer out)

1
2
3
4
5
public void writeTo(Writer out) throws IOException {
synchronized (lock) {
out.write(buf, 0, count);
}
}

  将当前缓冲区buffer中的内容写入到另一个字符输出流中。

public void write(int c)

1
2
3
4
5
6
7
8
9
10
public void write(int c) {
synchronized (lock) {
int newcount = count + 1;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
buf[count] = (char)c;
count = newcount;
}
}

  向缓冲区buf中写入一个字符。通过使用synchronized关键字可以保证多线程环境下的线程安全。在操作时,首先计算新的buf尾部边界,如果新的尾部边界大于当前buf的长度,那么执行扩容处理。扩容原则是新的buf数组长度是当前buf长度的两倍。然后将入参字符c写入保存到底层缓冲区buf中,同时更新buf的count值。

public void write(char c[], int off, int len)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void write(char c[], int off, int len) {
if ((off < 0) || (off > c.length) || (len < 0) ||
((off + len) > c.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
synchronized (lock) {
int newcount = count + len;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
System.arraycopy(c, off, buf, count, len);
count = newcount;
}
}

  将字符数组c中的内容写入到缓冲区buf中。第2 ~ 7行代码用来完成参数的有效性校验,避免发生越界溢出。通过使用synchronized关键字可以保证多线程环境下的线程安全。第9行代码计算存储数组c需要的空间,如果空间不足,需要对buf进行扩容处理。扩容原则是新的buf数组长度是当前buf长度的两倍。接着通过调用System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)方法将数组c中的内容写入到buf中,最后更新count值。

public void write(String str, int off, int len)

1
2
3
4
5
6
7
8
9
10
public void write(String str, int off, int len) {
synchronized (lock) {
int newcount = count + len;
if (newcount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
}
str.getChars(off, off + len, buf, count);
count = newcount;
}
}

  将字符串str中自off位置起长度为len的内容写入到缓冲区buf中。通过使用synchronized关键字可以保证多线程环境下的线程安全。第3行计算需要的存储空间,如果空间不足,需要对buf进行扩容处理。扩容原则是新的buf数组长度是当前buf长度的两倍。然后将str自off位置起长度为len的内容写入到缓冲区buf中,最后更新count值。

public CharArrayWriter append(CharSequence csq)

1
2
3
4
5
public CharArrayWriter append(CharSequence csq) {
String s = (csq == null ? "null" : csq.toString());
write(s, 0, s.length());
return this;
}

  将CharSequence中包含的内容写入到缓冲区buf中,如果csq为null,那么就将“null”这四个字符写入到buf中。底层调用的方法是write(String str, int off, int len)

public CharArrayWriter append(CharSequence csq, int start, int end)

1
2
3
4
5
public CharArrayWriter append(CharSequence csq, int start, int end) {
String s = (csq == null ? "null" : csq).subSequence(start, end).toString();
write(s, 0, s.length());
return this;
}

  将CharSequence中包含的内容写入到缓冲区buf中。实际需要写入的内容包含在start位置到end位置之间。底层调用的方法是write(String str, int off, int len)

public CharArrayWriter append(char c)

1
2
3
4
public CharArrayWriter append(char c) {
write(c);
return this;
}

  将一个字符写入到底层缓冲区buf中。底层调用的方法是write(int c)

public void reset()

1
2
3
public void reset() {
count = 0;
}

  将count值归零。可以直接通过覆盖的方式存储新的数据内容而不用重复分配新的数组空间。

public char toCharArray()[]

1
2
3
4
5
public char toCharArray()[] {
synchronized (lock) {
return Arrays.copyOf(buf, count);
}
}

  将当前缓冲区buf中的内容复制一份并返回其复制内容。

public int size()

1
2
3
public int size() {
return count;
}

  返回当前缓冲区buf中的数据长度。

public String toString()

1
2
3
4
5
public String toString() {
synchronized (lock) {
return new String(buf, 0, count);
}
}

  将当前缓冲区buf中的内容转换成一个String字符串并返回。

public void flush()

1
public void flush() { }

  将缓冲区buf中的内容推到目标输出位置上。方法体是个空方法,不执行任何操作。

public void close()

1
public void close() { }

  关闭当前输出流。方法体是个空方法,不执行任何操作。

涉及基础知识点

  1. NIL

参考文献

  1. NIL




------------- End of this article, thanks! -------------


  版权声明:本文由N.C.Lee创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处。
  本文作者为 N.C.Lee
  本文标题为 Java I/O 18 - CharArrayReader & CharArrayWriter
  本文链接为 https://marcuseddie.github.io/2018/java-CharArrayReader-CharArrayWriter.html