核心差異總覽
| 特性 | OutputStreamWriter | BufferedWriter |
| 預設緩衝區大小 | 8 KB (bytes) | 8 KB (chars) |
| 底層實作 | StreamEncoder (charset 編碼) | 字元緩衝 + 下游 Writer |
| 編碼轉換 | 內建(支援跨 charset 寫入) | 無(假設上游已處理編碼) |
| 錯誤字元處理 | 替換為 malformed surrogate | 不處理(交由上游) |
| 封裝對象 | OutputStream (位元組流) | Writer (字元流) |
兩層緩衝架構解析
應用程式
↓ write(char[])
BufferedWriter (字元緩衝, 8KB chars)
↓ write(char[], offset, len) / flushBuffer()
OutputStreamWriter (無額外字元緩衝,直接編碼)
↓ write(byte[]) / flush()
StreamEncoder → ByteBuffer → Channel
↓
作業系統檔案系統緩衝 (Page Cache, 通常 4KB+)
↓
磁碟控制器緩衝
↓
物理磁碟
關鍵釐清
- OutputStreamWriter 無字元級緩衝:其 8KB 為位元組級編碼緩衝(StreamEncoder 內部),非字元緩衝
- BufferedWriter 提供字元級緩衝:批次累積字元,減少下游
write(char[]) 呼叫次數
- OS 檔案系統緩衝為第三層:與 Java 類別無關,所有檔案 I/O 皆經過此層
實務選擇指南
場景一:需指定編碼寫入檔案
// 正確:指定 UTF-8 編碼寫入
try (OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("file.txt"), StandardCharsets.UTF_8);
BufferedWriter bw = new BufferedWriter(osw)) {
bw.write("中文內容");
bw.newLine();
}
場景二:高頻小量寫入(日誌、CSV)
// 必須加 BufferedWriter,否則每次 write 觸發編碼+系統呼叫
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("log.txt"), "UTF-8"),
64 * 1024)) { // 擴大緩衝至 64KB
for (String line : lines) {
bw.write(line);
bw.newLine();
}
}
場景三:Socket 通訊
- BufferedWriter 可控制封包大小(配合
flush())
- OutputStreamWriter 適用於二進制協議混合字元流
常見誤區
- 誤以為 OutputStreamWriter 有字元緩衝 → 實際上每次
write(char[]) 幾乎直接編碼並下發
- 雙重包裝 BufferedWriter(BufferedWriter(...)) → 無意義,只增加記憶體複製
- 忽略 flush() → BufferedWriter 緩衝區未滿時資料不會送出