Java IO 中的 Reader(java.io.Reader)和 Writer(java.io.Writer)类的工作原理与 InputStream 和 OutputStream 非常相似,不同之处在于 Reader 和 Writer 都是基于字符的,用于读写文本。InputStream 和 OutputStream 是基于字节的,还记得吗?
Java Reader 是 Java IO API 中所有 Reader 的基类。子类包括 BufferedReader、PushbackReader、InputStreamReader、StringReader 等,如下图:

下面是一个简单的 Reader 示例:
package com.hxstrive.java_io.demo01;
import java.io.FileReader;
import java.io.Reader;
/**
* FileReader 示例
* @author hxstrive.com
*/
public class FileReaderDemo {
public static void main(String[] args) {
// 从 input.txt 文件读取数据
try(Reader reader = new FileReader("C:\\Users\\Administrator\\Desktop\\input.txt")) {
int data = reader.read(); // 一次读取一个字符
while(data != -1){
char dataChar = (char) data;
System.out.print(dataChar); // 打印
data = reader.read();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}请注意,InputStream 一次返回一个字节,即一个介于 0 和 255 之间的值,如果数据流没有更多数据,则返回 -1。而 Reader 一次返回一个字符,即一个介于 0 和 65535 之间的值,如果数据流没有更多数据,则返回 -1。这并不一定意味着 Reader 每次从所连接的源读取两个字节。它可能一次读取一个或多个字节,这取决于所读取文本的编码。如 UTF-8 编码。
UTF-8 是一种变长字符编码,它可以使用 1 到 4 个字节来表示一个字符。下面为你分别列举一个字节、两个字节、三个字节的 UTF-8 编码示例:
一个字节的 UTF-8 编码用于表示 ASCII 字符,范围是从 U+0000 到 U+007F。最高位为 0,后面 7 位用来表示字符。例如,字符“A”的 ASCII 码是 65,对应的二进制是 01000001,UTF-8 编码下就是一个字节,十六进制为 41。
两个字节的 UTF-8 编码用于表示范围从 U+0080 到 U+07FF 的字符。第一个字节的前两位是 110,第二个字节的前两位是 10。例如,字符“¢”(美分符号)的 Unicode 码点是 U+00A2,对应的二进制是 10100010。在 UTF-8 编码中,它被编码为两个字节,十六进制为 c2a2。
三个字节的 UTF-8 编码用于表示范围从 U+0800 到 U+FFFF 的字符。第一个字节的前三位是 1110,第二个和第三个字节的前两位是 10。例如,字符“中”的 Unicode 码点是 U+4E2D,对应的二进制是 0100111000101101。在 UTF-8 编码中,它被编码为三个字节,十六进制为 e4b8ad。
四个字节的 UTF-8 编码用于表示范围从 U+10000 到 U+10FFFF 的字符,这种字符通常是一些特殊符号、表情符号等。第一个字节的前四位是 11110,后面三个字节的前两位是 10。例如,字符“😀”(笑脸表情)的 Unicode 码点超出了 U+FFFF 范围,需要用四个字节来进行 UTF-8 编码。
Java 中,可以将 Reader 与 InputStream 结合使用。如果你有一个 InputStream 并想从中读取字符,你可以将其封装在一个 InputStreamReader 中。像这样将 InputStream 传递给 InputStreamReader 的构造函数。例如:
package com.hxstrive.java_io.demo01;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* FileReader 示例
* @author hxstrive.com
*/
public class FileReaderDemo2 {
public static void main(String[] args) {
// 创建一个 InputStreamReader
try(Reader reader = new InputStreamReader(
new FileInputStream("C:\\Users\\Administrator\\Desktop\\input.txt"), "UTF-8")) {
int data = reader.read(); // 读取数据
while(data != -1){
char dataChar = (char) data;
System.out.print(dataChar);
data = reader.read();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}上例中,在构造函数中,你还可以指定使用什么字符集来解码文本等,例如:

Java 中,Writer 类是 Java IO API 中所有 Writer 的基类。子类包括 BufferedWriter 和 PrintWriter 等。如下图:

下面是一个简单的 Writer 示例:
package com.hxstrive.java_io.demo01;
import java.io.FileWriter;
import java.io.Writer;
/**
* FileWriter 示例
* @author hxstrive.com
*/
public class FileWriterDemo {
public static void main(String[] args) {
try(Writer writer = new FileWriter("C:\\Users\\Administrator\\Desktop\\output.txt")) {
writer.write("Hello World Writer"); // 将字符串写入到文件
} catch (Exception e) {
e.printStackTrace();
}
}
}运行结果:

Java 中,Writer 也可以与 OutputStream 结合使用,就像 Readers 和 InputStream 一样。将 OutputStream 包在 OutputStreamWriter 中,写入 Writer 的所有字符都会传递到 OutputStream。下面是一个 OutputStreamWriter 实例:
package com.hxstrive.java_io.demo01;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
* FileWriter 示例
* @author hxstrive.com
*/
public class FileWriterDemo2 {
public static void main(String[] args) {
try(Writer writer = new OutputStreamWriter(
new FileOutputStream("C:\\Users\\Administrator\\Desktop\\output.txt"), "UTF-8")) {
writer.write("你好!");
} catch (Exception e) {
e.printStackTrace();
}
}
}运行结果:

更多 OutputStreamWriter 构造方法,如下图:

与流一样,Reader 和 Writer 可以组合成链,以实现更强大的 IO。其工作原理就像将 Reader 与 InputStream 或 Writer 与 OutputStream 结合在一起一样。
例如,你可以将一个 Reader 封装在一个 BufferedReader 中,或将一个 Writer 封装在一个 BufferedWriter 中,从而实现缓冲功能,提升写入速度:
package com.hxstrive.java_io.demo01;
import java.io.*;
/**
* FileWriter 示例
* @author hxstrive.com
*/
public class FileWriterDemo3 {
public static void main(String[] args) {
try(BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream("C:\\Users\\Administrator\\Desktop\\input.txt"), "UTF-8"));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("C:\\Users\\Administrator\\Desktop\\output.txt"), "UTF-8"))) {
// 读取文件内容,并写入到输出文件中
String line;
while((line = reader.readLine()) != null) {
writer.write(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}运行结果:

关于 Reader 和 Writer,以及他们子类的更多知识请阅读后续章节。