Java I/O 採用裝飾器模式串聯多層流(如 BufferedOutputStream → FileOutputStream),資料在層層緩衝中流動。若忘記 flush() 或 close(),資料卡在緩衝區未寫入磁碟,傳統除錯極難定位是哪一層「吃掉」了資料。
自訂 FilterOutputStream 子類,計數實際寫入的位元組數,插入流鏈中即時監控:
public class CountingOutputStream extends FilterOutputStream {
private long count = 0;
public CountingOutputStream(OutputStream out) { super(out); }
@Override public void write(byte[] b) throws IOException {
count += b.length; super.write(b);
}
@Override public void write(byte[] b, int off, int len) throws IOException {
count += len; super.write(b, off, len);
}
@Override public void write(int b) throws IOException {
count++; super.write(b);
}
public long getCount() { return count; }
}
// 問題代碼:資料卡在 BufferedOutputStream 緩衝區
File file = new File("test.dat");
byte[] data = "Hello".getBytes();
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
out.write(data); // 忘記 flush/close
// 檔案大小為 0
// 插入計數流定位
CountingOutputStream cos = new CountingOutputStream(new FileOutputStream(file));
OutputStream out2 = new BufferedOutputStream(cos);
out2.write(data);
System.err.println("實際寫入位元組: " + cos.getCount()); // 輸出 0 → 確認 BufferedOutputStream 未刷新
// 加上 out2.flush() 後再查,輸出 5 → 正常
當流鏈超過 4 層(如 GZIPOutputStream → CipherOutputStream → BufferedOutputStream → FileOutputStream),可在任兩層之間插入 CountingOutputStream,二分法快速鎖定問題層級。同理可實作 CountingInputStream 監控讀取端。
MeteredStream 整合 Micrometer/Prometheus 指標try-with-resources 確保流正確關閉,避免資源洩漏「流除錯」思維:將不可見的資料流動可視化,是排查 I/O 疑難雜症的關鍵技能。