首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

【Java I/O】字元輸出流 Writer 完整指南與核心類別詳解

     編輯:Java 綜合教學

前言

在 Java I/O 體系中,字元輸出流 Writer 與字元輸入流 Reader 互為對應,兩者在設計模式與類別結構上高度相似。本文將系統梳理 Writer 及其核心子類的用途、結構關係與實戰範例,幫助開發者快速掌握字元輸出的最佳實踐。

一、Writer 體系核心類別總覽

每個字元輸出流類別都針對特定場景設計,主要分類如下:

  • Writer:字元輸出流抽象基類,定義寫入、刷新、關閉等核心契約。
  • OutputStreamWriter:橋接位元組流與字元流,將字元編碼為位元組寫入底層 OutputStream。
  • FileWriter:便利類,直接向本地檔案寫入字元,內部封裝 FileOutputStream。
  • CharArrayWriter:以字元陣列 (char[]) 作為緩衝區,支援動態擴容,適合記憶體內字串構建。
  • PipedWriter:配合 PipedReader 實現同一 JVM 內執行緒間的管道通訊。
  • StringWriter:內部使用 StringBuffer,適合高效拼裝字串。
  • BufferedWriter:裝飾者模式經典應用,為任意 Writer 增加緩衝區,減少實際 I/O 呼叫,提升效能;新增 newLine() 等實用方法。
  • FilterWriter:抽象裝飾基類,供開發者實作自訂過濾邏輯(實務上較少直接使用)。
  • PrintWriter:功能最豐富的格式化輸出流,支援 printf、println 等便利方法,且不拋出受檢異常。

二、類別結構圖

下圖展示 Writer 及 Reader 的繼承與裝飾關係(含核心子類):

Writer/Reader 類別結構圖

三、核心類別深度解析與程式碼範例

1. OutputStreamWriter —— 位元組流轉字元流的橋樑

OutputStreamWriter 的每個建構子都要求傳入一個 OutputStream,底層委派給 StreamDecoder 處理字元編碼。典型用法是包裝標準輸出或網路/檔案位元組流:

Writer writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8);
writer.write("Hello, World!");
writer.flush(); // 確保緩衝資料送達底層流
writer.close();

關鍵點:指定 Charset 避免平台預設編碼導致亂碼;flush() 僅刷新字元緩衝,close() 會關閉底層位元組流。

2. FileWriter —— 檔案字元寫入的首選

FileWriter 繼承 OutputStreamWriter,建構子內部自動建立 FileOutputStream。適用於直接寫入文字檔:

try (Writer writer = new FileWriter("C:\\data\\output.txt", StandardCharsets.UTF_8)) {
    writer.write('A');
    writer.write("中文測試\n");
    writer.flush();
}

注意:JDK 11+ 建構子支援直接指定 Charset;舊版本需自行包裝 OutputStreamWriter。

3. CharArrayWriter —— 記憶體內字元緩衝

內部維護可擴容的 char[],適合「先寫入記憶體,再一次性取出」的場景:

CharArrayWriter writer = new CharArrayWriter(1024);
writer.write("動態擴容測試");
char[] result = writer.toCharArray(); // 取得最終字元陣列
String str = writer.toString();       // 直接轉字串

4. PipedWriter / PipedReader —— 執行緒管道通訊

必須配對使用,PipedWriter 連接 PipedReader(或反向),實現生產者-消費者模型。讀寫雙方均會阻塞,直到資料可用或緩衝區有空間:

PipedWriter pw = new PipedWriter();
PipedReader pr = new PipedReader(pw);
// 執行緒 A 寫入
new Thread(() -> {
    try { pw.write("訊息內容"); pw.close(); } catch (IOException e) { e.printStackTrace(); }
}).start();
// 執行緒 B 讀取
new Thread(() -> {
    try { char[] buf = new char[1024]; int len = pr.read(buf); System.out.println(new String(buf, 0, len)); }
    catch (IOException e) { e.printStackTrace(); }
}).start();

5. BufferedWriter —— 效能優化的裝飾者

為任意 Writer 增加緩衝區(預設 8KB,可自訂大小),大幅減少系統呼叫:

try (BufferedWriter bw = new BufferedWriter(
        new FileWriter("log.txt", StandardCharsets.UTF_8), 16 * 1024)) {
    bw.write("第一行");
    bw.newLine(); // 跨平台換行
    bw.write("第二行");
}

6. StringWriter —— 字串構建器的流式介面

內部委派 StringBuffer,提供 Writer 介面,方便 API 互通:

StringWriter sw = new StringWriter();
sw.write("Hello ");
sw.write("World");
String result = sw.toString(); // "Hello World"

7. PrintWriter —— 格式化輸出的瑞士刀

支援 printf、format、println 等豐富方法,且吞噬受檢異常(需透過 checkError() 檢查):

PrintWriter pw = new PrintWriter(System.out, true, StandardCharsets.UTF_8); // autoFlush=true
pw.printf("編號: %05d, 價格: %.2f%n", 101, 99.9);
pw.println("自動換行並刷新");

四、最佳實踐總結

  1. 指定編碼:優先使用 JDK 11+ 的 FileWriter(path, charset) 或手動包裝 OutputStreamWriter。
  2. 善用 try-with-resources:確保流正確關閉,釋放檔案鎖與系統資源。
  3. 大量寫入必加緩衝:BufferedWriter 是效能提升的最低成本手段。
  4. 區分 flush 與 close:flush() 僅推送字元緩衝;close() 會關閉底層位元組流,後續不可再寫入。
  5. 執行緒管道:PipedWriter/Reader 僅適用於同一 JVM 內執行緒,跨進程請用 Socket 或檔案。

五、常見問題 FAQ

Q: Writer 與 OutputStream 如何選擇?
A: 處理文字(字元、字串)用 Writer 體系;處理二進位資料(圖片、影音、序列化位元組)用 OutputStream 體系。兩者可透過 OutputStreamWriter/InputStreamReader 互轉。
Q: PrintWriter 為何不拋 IOException?
A: 設計初衷是簡化輸出程式碼(如日誌、控制台列印),錯誤狀態保存在內部旗標,呼叫 checkError() 檢查即可。
Q: BufferedWriter 的緩衝區大小如何設定?
A: 一般 8KB~64KB 即可;若寫入超大檔案可適當調大(如 256KB),但過大反而增加記憶體壓力與 GC 負擔。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved