Java IO 系統是處理資料輸入輸出的基礎設施,理解其架構對編寫高效、健壯的 IO 程式碼至關重要。本文系統梳理流的分類、裝飾器模式應用、RandomAccessFile 特性,並附完整範例程式碼。
Java 中所有流均繼承自四大抽象基類:InputStream、OutputStream(位元組流)、Reader、Writer(字元流)。
以位元組(8 bits)為單位讀寫,適用於二進位資料(圖片、聲音、序列化物件等)。
| 輸入流 | 用途 |
|---|---|
| ByteArrayInputStream | 將記憶體緩衝區作為輸入來源 |
| StringBufferInputStream | 將 String 物件作為輸入來源(已過時) |
| FileInputStream | 讀取檔案 |
| PipedInputStream | 執行緒間管道通訊 |
| SequenceInputStream | 合併多個輸入流 |
| 輸出流 | 用途 |
|---|---|
| ByteArrayOutputStream | 寫入記憶體緩衝區 |
| FileOutputStream | 寫入檔案 |
| PipedOutputStream | 執行緒間管道通訊 |
| SequenceOutputStream | 合併多個輸出流 |
以 Unicode 字元(16 bits)為單位,專為文字資料設計,內部自動處理編碼轉換。
| 讀取器 | 對應位元組流 |
|---|---|
| CharArrayReader | ByteArrayInputStream |
| StringReader | StringBufferInputStream |
| FileReader | FileInputStream |
| PipedReader | PipedInputStream |
| 寫入器 | 對應位元組流 |
|---|---|
| CharArrayWriter | ByteArrayOutputStream |
| StringWriter | (無直接對應) |
| FileWriter | FileOutputStream |
| PipedWriter | PipedOutputStream |
InputStreamReader:位元組輸入流 → 字元讀取器OutputStreamWriter:位元組輸出流 → 字元寫入器FilterInputStream / FilterOutputStream 及其子類實現裝飾器模式,無需修改原始流即可增強功能。
| 類別 | 功能 | 適用流類型 |
|---|---|---|
| BufferedInputStream / BufferedReader | 緩衝讀取,提升效能 | 位元組 / 字元 |
| DataInputStream / DataOutputStream | 讀寫基本型別 | 位元組 |
| LineNumberInputStream / LineNumberReader | 追蹤行號 | 位元組 / 字元 |
| PushbackInputStream / PushbackReader | 回推資料,用於編譯器 | 位元組 / 字元 |
| PrintStream / PrintWriter | 格式化輸出 | 位元組 / 字元 |
實戰場景:若需「先緩存再寫入檔案」,只需將 FileOutputStream 包裝為 BufferedOutputStream,再包裝為 DataOutputStream 即可同時獲得緩衝與基本型別寫入能力。
"r"(唯讀)、"rw"(讀寫)seek(long pos) 跳轉import java.io.*;
public class IODemo {
public static void main(String[] args) throws IOException {
// 1. 緩衝逐行讀取檔案
StringBuilder content = new StringBuilder();
try (BufferedReader in = new BufferedReader(new FileReader("TestIO.java"))) {
String line;
while ((line = in.readLine()) != null) content.append(line).append("\n");
}
// 2. 從標準輸入讀取
try (BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
System.out.println("Enter a line:");
System.out.println(stdin.readLine());
}
// 3. 從字串讀取
try (StringReader sr = new StringReader(content.toString())) {
int c;
while ((c = sr.read()) != -1) System.out.print((char) c);
}
// 4. 格式化寫入檔案
try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("TestIO.out")))) {
try (BufferedReader br = new BufferedReader(new StringReader(content.toString()))) {
String line; int lineNo = 1;
while ((line = br.readLine()) != null) out.println(lineNo++ + ":" + line);
}
}
// 5. 基本型別序列化與反序列化
try (DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")))) {
dos.writeDouble(Math.PI);
dos.writeChars("Pi via writeChars\n");
dos.writeBytes("Pi via writeBytes\n");
}
try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")))) {
System.out.println(dis.readDouble());
try (BufferedReader br = new BufferedReader(new InputStreamReader(dis))) {
System.out.println(br.readLine());
System.out.println(br.readLine());
}
}
// 6. RandomAccessFile 隨機存取
try (RandomAccessFile raf = new RandomAccessFile("rtest.dat", "rw")) {
for (int i = 0; i < 10; i++) raf.writeDouble(i * 1.414);
}
try (RandomAccessFile raf = new RandomAccessFile("rtest.dat", "r")) {
for (int i = 0; i < 10; i++) System.out.println("Value " + i + ":" + raf.readDouble());
}
try (RandomAccessFile raf = new RandomAccessFile("rtest.dat", "rw")) {
raf.seek(5 * 8); // 定位第 6 個 double
raf.writeDouble(47.0001);
}
}
}
Buffered*)大幅提升 IO 效能RandomAccessFile 獨立於四大流體系,提供隨機存取能力