Java I/O 15 - Reader & Writer

  关于 java.io.Reader java.io.Writer 的部分笔记,这两个类是所有的字符流读写类的父类。继承这两个类的子类出于性能等方面的考虑会重写其方法。本文演示代码段的执行环境基于JDK版本1.7

概述

  Reader和Writer是用来完成关于字符流读写操作的公共父类。继承这两个类的子类分别实现读写操作和关闭操作。虽然自身实现了部分的读写方法,但是出于高效、额外功能的考虑,子类需要重写覆盖Reader和Writer自身已经实现的方法来获取更好的使用性能。

  在使用Writer向底层输出流输出数据内容时,Writer仅保证通过flush操作可以将数据从内存输出给操作系统,但是并不保证数据可以正确完整的写入到特定的物理设备中,这一点在使用时需要加以注意。

继承关系

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

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

实现接口

类名 实现接口
Reader Closeable, AutoCloseable,Readable
Writer Closeable, Flushable, AutoCloseable, Appendable

Reader

Constructor Summary

protected Reader()

1
2
3
protected Reader() {
this.lock = this;
}

protected Reader(Object lock)

1
2
3
4
5
6
protected Reader(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}

部分方法

public int read(java.nio.CharBuffer target)

1
2
3
4
5
6
7
8
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;
}

  从底层读取数据并保存到一个CharBuffer对象中。实际读取的长度由CharBuffer对象的剩余可用空间决定。通过调用方法read(char cbuf[], int off, int len)读取到数据后再封装回CharBuffer对象,最后返回实际读取的字符长度。

public int read()

1
2
3
4
5
6
7
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}

  从底层读取一个字符并返回。实际调用的方法是read(char cbuf[])。

public int read(char cbuf[])

1
2
3
4
5
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}

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

  从底层输入流中读取数据并将读取的数据保存到数组cbuf中。实际调用的方法是read(char cbuf[], int off, int len),该方法被声明为抽象方法,需要在子类中实现。

public long skip(long n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
}

  跳过长度为n的字节数并开始从第n+1个位置读取数据。代码段通过synchronized保证了多线程环境的线程安全要求。第6 ~ 7行代码计算理论上需要跳过的数据长度并初始化一个数据存储buffer空间。第9 ~ 14行则循环从输入流中读取数据并将读取的数据保存到数组buffer中,如果读取结束则退出循环。最后计算实际跳过的长度并返回给方法调用方,需要注意的是实际跳过的长度有可能小于入参长度n。

public boolean ready()

1
2
3
public boolean ready() throws IOException {
return false;
}

  判断当前输入流是否可以通过read()方法获取数据。

public boolean markSupported()

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

  是否支持标记重读。Reader自身不支持标记重读操作,所以返回false给接口调用方。

public void mark(int readAheadLimit)

1
2
3
public void mark(int readAheadLimit) throws IOException {
throw new IOException("mark() not supported");
}

  标记重读的起点。Reader类自身不支持标记重复读取字符的起点,所以直接抛出异常给方法调用方。

public void reset()

1
2
3
public void reset() throws IOException {
throw new IOException("reset() not supported");
}

  重置下一个字符读取位置。Reader类自身不支持重置操作,所以直接抛出异常给方法调用方。

abstract public void close()

1
abstract public void close() throws IOException;

  关闭流并释放占用资源。该方法是一个抽象方法,需要子类去实现具体的关闭流程。

Writer

Constructor Summary

protected Writer()

1
2
3
protected Writer() {
this.lock = this;
}

protected Writer(Object lock)

1
2
3
4
5
6
protected Writer(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}

部分方法

public void write(int c)

1
2
3
4
5
6
7
8
9
public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[writeBufferSize];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0, 1);
}
}

  向缓冲区buffer中写入一个字符数据。该方法被synchronized关键字包围起来保证了多线程环境下的线程安全。缓冲区buffer数组的长度默认为1024。该方法已经被java.io.OutputStreamWriter等子类重写覆盖。

public void write(char cbuf[])

1
2
3
public void write(char cbuf[]) throws IOException {
write(cbuf, 0, cbuf.length);
}

  向缓冲区buffer中写入数组cbuf中的字符数据。该方法通过调用方法write(char cbuf[], int off, int len)实现相关操作。

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

1
abstract public void write(char cbuf[], int off, int len) throws IOException;

  向缓冲区buffer中写入数组cbuf中包含的字符数据。该方法被声明为一个抽象方法,需要在子类中实现该方法。

public void write(String str)

1
2
3
public void write(String str) throws IOException {
write(str, 0, str.length());
}

  将一个String字符串写入到缓冲区buffer中。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void write(String str, int off, int len) throws IOException {
synchronized (lock) {
char cbuf[];
if (len <= writeBufferSize) {
if (writeBuffer == null) {
writeBuffer = new char[writeBufferSize];
}
cbuf = writeBuffer;
} else { // Don't permanently allocate very large buffers.
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0);
write(cbuf, 0, len);
}
}

  将一个String字符串中自下标off位置起长度为len的内容写入到缓冲区buffer中。通过将String字符串转成char数组,再调用write(char cbuf[], int off, int len)方法完成操作。该方法已经被java.io.OutputStreamWriter等子类重写覆盖。

public Writer append(CharSequence csq)

1
2
3
4
5
6
7
public Writer append(CharSequence csq) throws IOException {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}

  将一个CharSequence对象中包含的内容写入到底层输出流的缓冲区buffer中。该方法的实际效果等同于write(String str),其底层实现也是通过将CharSequence转成String字符串,然后在调用上述方法来完成操作的。

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

1
2
3
4
5
public Writer append(CharSequence csq, int start, int end) throws IOException {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}

  将一个CharSequence对象中从起始位置start到结束end之间的内容写入到底层输出流的缓冲区buffer中。

public Writer append(char c)

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

  将一个字符写入到底层输出了的缓冲区buffer中。该方法的实际效果等同于write(int c)

abstract public void flush()

1
abstract public void flush() throws IOException;

  将buffer缓冲区中保存的数据推到底层输出流中。该方法是一个抽象方法,具体流程需要由子类自行完成实现。

abstract public void close()

1
abstract public void close() throws IOException;

  关闭当前输出流。该方法是一个抽象方法,具体关闭流的流程需要由子类自行完成实现。

涉及基础知识点

  1. NIL

参考文献

  1. NIL



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


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