Reader是Java的IO库提供的另一个输入流接口。和InputStream的区别是,InputStream是一个字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单位读取
Reader 中的read()方法读取字符流的下一个字符,并返回字符表示的int,范围是0~65535。如果已读到末尾,返回-1。
总是使用try (resource)保证Reader正确关闭。

Reader

FileReader

FileReader是Reader的一个子类,直接继承InputStreamReader,它可以打开文件并获取Reader。
如果我们读取一个纯ASCII编码的文本文件,上述代码工作是没有问题的。但如果文件中包含中文,就会出现乱码,因为FileReader默认的编码与系统相关,例如,Windows系统的默认编码可能是GBK,打开一个UTF-8编码的文本文件就会出现乱码。
要避免乱码问题,我们需要在创建FileReader时指定编码:

  1. ublic void readFile() throws IOException {
  2. // 创建一个FileReader对象:
  3. Reader reader = new FileReader("src/readme.txt"); // 字符编码是???
  4. for (;;) {
  5. int n = reader.read(); // 反复调用read()方法,直到返回-1
  6. if (n == -1) {
  7. break;
  8. }
  9. System.out.println((char)n); // 打印char
  10. }
  11. reader.close(); // 关闭流
  12. }

和InputStream类似,Reader也是一种资源,需要保证出错的时候也能正确关闭,所以我们需要用try (resource)来保证Reader在无论有没有IO错误的时候都能够正确地关闭:

  1. try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8) {
  2. // TODO
  3. }

CharArrayReader

CharArrayReader可以在内存中模拟一个Reader,它的作用实际上是把一个char[]数组变成一个Reader,这和ByteArrayInputStream非常类似:

  1. try (Reader reader = new CharArrayReader("Hello".toCharArray())) {
  2. }

StringReader

StringReader可以直接把String作为数据源,它和CharArrayReader几乎一样:

  1. try (Reader reader = new StringReader("Hello")) {
  2. }

InputStreamReader

Reader和InputStream有什么关系?

除了特殊的CharArrayReader和StringReader,普通的Reader实际上是基于InputStream构造的,因为Reader需要从InputStream中读入字节流(byte),然后,根据编码设置,再转换为char就可以实现字符流。如果我们查看FileReader的源码,它在内部实际上持有一个FileInputStream。

既然Reader本质上是一个基于InputStream的byte到char的转换器,那么,如果我们已经有一个InputStream,想把它转换为Reader,是完全可行的。InputStreamReader就是这样一个转换器,它可以把任何InputStream转换为Reader。示例代码如下:

  1. // 持有InputStream:
  2. InputStream input = new FileInputStream("src/readme.txt");
  3. // 变换为Reader:
  4. Reader reader = new InputStreamReader(input, "UTF-8");


构造InputStreamReader时,我们需要传入InputStream,还需要指定编码,就可以得到一个Reader对象。上述代码可以通过try (resource)更简洁地改写如下:

  1. try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
  2. // TODO:
  3. }


上述代码实际上就是FileReader的一种实现方式。

使用try (resource)结构时,当我们关闭Reader时,它会在内部自动调用InputStream的close()方法,所以,只需要关闭最外层的Reader对象即可。

使用InputStreamReader,可以把一个InputStream转换成一个Reader

Writer

Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。
WriterOutputStream区别如下:

OutputStream Writer
字节流,以byte为单位 字符流,以char为单位
写入字节(0~255):void write(int b) 写入字符(0~65535):void write(int c)
写入字节数组:void write(byte[] b) 写入字符数组:void write(char[] c)
无对应方法 写入String:void write(String s)


Writer是所有字符输出流的超类,它提供的方法主要有:

  • 写入一个字符(0~65535):void write(int c);
  • 写入字符数组的所有字符:void write(char[] c);
  • 写入String表示的所有字符:void write(String s)。