在 Java I/O 體系中,字符輸入流 Reader 扮演著核心角色。本文將系統梳理 Reader 的繼承體系與核心實現類,幫助開發者快速建立完整的知識框架。以下是 Reader 的主要子類分類:

Reader —— 所有字符輸入流的根節點InputStreamReader —— 將字節流解碼為字符流FileReader —— 直接讀取本地字符文件CharArrayReader —— 以字符數組為數據源PipedReader —— 配合 PipedWriter 實現同一 JVM 內線程通信BufferedReader —— 提供行讀取等高級功能FilterReader —— 自定義過濾邏輯的基類StringReader —— 將字符串作為輸入源PushbackReader(支持回退)、LineNumberReader(行號追蹤)InputStreamReader 是連接字節流與字符流的關鍵橋樑。其每個構造方法均接收一個 InputStream 參數,底層通過 StreamDecoder 實現解碼邏輯(包括構造時的 StreamDecoder.forInputStreamReader 與讀取時的 StreamDecoder.read)。

提示:有興趣深入了解解碼機制的讀者可自行研讀 StreamDecoder 源碼。
package io;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class IOTest {
public static void main(String[] args) throws IOException {
// 將標準字節輸入流包裝為字符輸入流
Reader reader = new InputStreamReader(System.in);
// 讀取單個字符並輸出
System.out.println((char) reader.read());
reader.close();
}
}
// 輸入範例:鍵盤輸入 0 → 輸出 0
FileReader 繼承自 InputStreamReader,專門用於讀取本地字符文件。其構造方法內部會先根據參數創建 FileInputStream,再調用父類構造方法完成字節到字符的轉換。

package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class IOTest {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("C:\\test.txt");
System.out.println((char) reader.read());
reader.close();
}
}
CharArrayReader 允許直接將字符數組作為輸入源。值得注意的是,它持有對外部數組的引用,因此數組內容的變更會即時反映到流的讀取結果中。

package io;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
public class IOTest {
public static void main(String[] args) throws IOException {
char[] charArr = {'a', 'b', 'c'};
Reader reader = new CharArrayReader(charArr);
// 修改原數組
charArr[0] = 'e';
// 讀取到的是修改後的 'e'
System.out.println((char) reader.read());
reader.close();
}
}
PipedReader 需與 PipedWriter 配合使用,實現同一 JVM 內兩個線程的字符數據傳輸。讀寫操作均為阻塞式,典型場景為生產者-消費者模型。
package io;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class IOTest {
public static void main(String[] args) throws IOException {
final PipedWriter pw = new PipedWriter();
final PipedReader pr = new PipedReader(pw);
ExecutorService es = Executors.newFixedThreadPool(2);
es.execute(() -> {
try { pw.write("hello~"); }
catch (IOException e) { e.printStackTrace(); }
});
es.execute(() -> {
char[] cbuffer = new char[6];
try { pr.read(cbuffer, 0, 6); }
catch (IOException e) { e.printStackTrace(); }
System.out.println(cbuffer);
});
}
}
採用裝飾器模式設計,BufferedReader 為被裝飾流提供字符緩衝區,避免頻繁訪問底層介質,並新增 readLine() 等實用方法。參考:裝飾器模式詳解
package io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
// 指定 10KB 緩衝區
BufferedReader reader = new BufferedReader(
new FileReader("C:\\test.txt"), 10 * 1024);
System.out.println(reader.readLine());
System.out.println(reader.readLine());
System.out.println(reader.readLine());
reader.close();
}
}
繼承自 BufferedReader,除保留所有功能外,新增行號追蹤能力。需注意 setLineNumber() 僅修改內部計數器,不會導致流跳轉。
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class IOTest {
public static void main(String[] args) throws IOException {
LineNumberReader reader = new LineNumberReader(
new FileReader("C:\\test.txt"), 10 * 1024);
System.out.println(reader.getLineNumber()); // 0
System.out.println(reader.readLine());
System.out.println(reader.getLineNumber()); // 1
System.out.println(reader.readLine());
reader.close();
}
}
抽象類 FilterReader 簡單覆寫了 Reader 的所有方法,作為實現自定義過濾邏輯的基類。雖然功能看似單薄,但為擴展提供了標準契約。
同樣基於裝飾器模式,PushbackReader 允許通過 unread() 將已讀字符推回緩衝區,下次讀取優先從緩衝區獲取。常用於需要「回頭看」的解析場景(如詞法分析)。
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;
public class IOTest {
public static void main(String[] args) throws IOException {
PushbackReader reader = new PushbackReader(
new FileReader("C:\\test.txt"), 1024);
char a = (char) reader.read();
System.out.println(a);
reader.unread(a); // 推回緩衝區
System.out.println((char) reader.read()); // 再次讀取同一字符
System.out.println((char) reader.read()); // 讀取下一字符
reader.close();
}
}
最輕量的實現,直接將字符串轉為字符輸入流,適合單元測試或簡單解析場景。
package io;
import java.io.IOException;
import java.io.StringReader;
public class IOTest {
public static void main(String[] args) throws IOException {
StringReader reader = new StringReader("hello~");
char[] cbuf = new char[6];
reader.read(cbuf, 0, 6);
System.out.println(cbuf);
reader.close();
}
}