系列第 58 篇,深入剖析 Java IO 中最核心、最易混淆的字節流與字符流體系,從二進制視角重構理解,徹底搞懂編碼、轉換、緩衝、裝飾模式。
文件在磁碟上只有位元組 (bytes),沒有字符 (chars)。 字符是內存中概念,位元組是存儲/傳輸概念。二者橋樑是 **編碼**。
二進制存儲 (DataOutputStream):
DataOutputStream out = new DataOutputStream(new FileOutputStream("test.dat"));
out.writeInt(123); // 寫入 4 字節: 00 00 00 7B (大端序)
UltraEdit 十六進制視圖:00 00 00 7B → 最低位字節 7B = 123。
文本存儲 (字符流):
OutputStream out = new FileOutputStream("test.txt");
String data = Integer.toString(123); // "123"
out.write(data.getBytes("UTF-8")); // 3 字節: 31 32 33
十六進制:31 32 33 → ASCII '1'=49(0x31), '2'=50(0x32), '3'=51(0x33)。
同一文本 "hello, 123, 老馬" 不同編碼位元組長度不同:
| 編碼 | 英文/數字 | 中文 | 總字節數 | 十六進制片段 |
|---|---|---|---|---|
| UTF-8 | 1 字節 | 3 字節 | 變長 | ![]() |
| GB18030 | 1 字節 | 2 字節 | 變長 | ![]() |
| UTF-16BE | 2 字節 | 2 字節 | 定長 | ![]() |
Java 內存中 char 固定 2 字節 (UTF-16BE),這是字符流操作的基礎單位。
| 維度 | 字節流 (Byte Stream) | 字符流 (Char Stream) |
|---|---|---|
| 基類 | InputStream / OutputStream | Reader / Writer (抽象類) |
| 處理單位 | byte (8 bits) | char (16 bits, UTF-16) |
| 編碼感知 | 無 | 有 (構造時指定或平台默認) |
| 適用場景 | 圖片、視頻、二進制文件、網絡傳輸 | 文本文件、配置文件、源代碼、日誌 |
| 按行讀取 | 不支援 | BufferedReader.readLine() 支援 |
Reader / Writer:所有字符流父類,定義 read()/write() 等核心協議InputStreamReader:位元組流 → 字符流,**解碼** (指定 Charset)OutputStreamWriter:字符流 → 位元組流,**編碼** (指定 Charset)FileReader / FileWriter:隱式使用平台默認編碼,**生產禁用**,跨平台亂碼根源new FileReader(file, StandardCharsets.UTF_8) (Java 11+) 或 手動鏈 new InputStreamReader(new FileInputStream(file), "UTF-8")CharArrayReader/Writer:char[] 為源/目標StringReader/Writer:String/StringBuilder 為源/目標BufferedReader / BufferedWriter:緩衝區 + readLine()/newLine(),大幅提升 IO 效能,支援按行處理PrintWriter:包裝 Writer,提供 print/println/printf 格式化輸出,**自動 flush 可選**System.in // InputStream (標準輸入,鍵盤) System.out // PrintStream (標準輸出,螢幕) - 字節流但可直接打印字符 System.err // PrintStream (標準錯誤,螢幕) - 同 out,獨立重定向 // 字符流包裝標準輸入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line = br.readLine();
Files.readString(path, StandardCharsets.UTF_8)Files.writeString(path, content, StandardCharsets.UTF_8)try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
lines.forEach(line -> process(line));
}
IOUtils.copy(input, output) (Commons IO) 或 input.transferTo(output) (Java 9+)FileReader/FileWriter、new String(bytes) 無參構造InputStreamReader/OutputStreamWriterreadAllBytes下一篇將探討:NIO.2 (Path/Files/AsynchronousFileChannel)、零拷貝、內存映射文件、Netty ByteBuf 與標準 IO 的轉換。