PrintStream 是位元組導向的列印輸出流,繼承自 FilterOutputStream。它包裝其他 OutputStream,提供豐富的 print()、println()、printf()、format() 方法,將各類資料自動轉為字串並編碼輸出。標準輸出 System.out 和標準錯誤 System.err 皆為 PrintStream 實例。核心特性:永不拋出 IOException(內部捕獲設置 trouble 旗標)、支援自動刷新、支援指定字元集。
| 建構子 | 輸出目標 | 自動刷新 | 字元集 |
|---|---|---|---|
| PrintStream(OutputStream out) | 位元組流 | false | 預設 |
| PrintStream(OutputStream out, boolean autoFlush) | 位元組流 | 可指定 | 預設 |
| PrintStream(OutputStream out, boolean autoFlush, String encoding) | 位元組流 | 可指定 | 指定名稱 |
| PrintStream(String fileName) | 檔案路徑 | false | 預設 |
| PrintStream(String fileName, String encoding) | 檔案路徑 | false | 指定名稱 |
| PrintStream(File file) | File 物件 | false | 預設 |
| PrintStream(File file, String encoding) | File 物件 | false | 指定名稱 |
autoFlush=true 時,println()、printf()、format()、write(byte[])、寫入換行符 '\n' 會觸發 flush()。注意:print() 不觸發,write(int) 僅寫入 '\n' 時觸發。
// 格式化輸出
PrintStream printf(String format, Object... args)
PrintStream printf(Locale l, String format, Object... args)
PrintStream format(String format, Object... args)
PrintStream format(Locale l, String format, Object... args)
// 列印方法(自動轉字串 → 編碼 → 寫入位元組)
void print(boolean b) / print(char c) / print(int i) / print(long l)
void print(float f) / print(double d) / print(char[] s) / print(String s) / print(Object obj)
void println() / println(boolean x) / println(char x) / println(int x) / println(long x)
void println(float x) / println(double x) / println(char[] x) / println(String x) / println(Object x)
// 位元組寫入(OutputStream 基礎方法)
void write(int b) // 單位元組
void write(byte[] buf, int off, int len) // 位元組陣列片段
// 追加與檢查
PrintStream append(char c) / append(CharSequence csq) / append(CharSequence csq, int start, int end)
boolean checkError() // 檢查內部 trouble 旗標
void flush() / void close()
// PrintStream 內部核心欄位
private final boolean autoFlush;
private boolean trouble = false;
private Formatter formatter;
private BufferedWriter textOut; // 字元緩衝寫入器
private OutputStreamWriter charOut; // 字元→位元組編碼器
// 建構子初始化編碼鏈路
private PrintStream(boolean autoFlush, OutputStream out) {
super(out);
this.autoFlush = autoFlush;
this.charOut = new OutputStreamWriter(this); // 使用預設編碼
this.textOut = new BufferedWriter(charOut); // 加入字元緩衝
}
// 寫入字元陣列:textOut → charOut → out (OutputStream)
private void write(char[] buf) {
try {
synchronized (this) {
ensureOpen();
textOut.write(buf);
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush) {
for (char c : buf) if (c == '\n') out.flush();
}
}
} catch (IOException x) { trouble = true; }
}
print("中文")
→ String.valueOf() → "中文"
→ write(String) → textOut.write("中文") (BufferedWriter)
→ textOut.flushBuffer() → charOut.write("中文") (OutputStreamWriter)
→ charOut.flushBuffer() → 以指定編碼(如 UTF-8)編碼為位元組
→ out.write(bytes) (底層 OutputStream,如 FileOutputStream)
| 特性 | PrintStream | PrintWriter | DataOutputStream |
|---|---|---|---|
| 資料單位 | 位元組 | 字元 | 位元組 |
| 編碼指定 | 建構子支援 | 建構子支援 | 固定 UTF-8 |
| 自動刷新 | 支援 | 支援 | 不支援 |
| 異常處理 | 吞噬,checkError() | 吞噬,checkError() | 拋出 IOException |
| 格式化 | printf/format | printf/format | 無 |
| 基本型別二進制寫入 | 無 | 無 | writeInt/Long/Double 等 |
| 主要用途 | 控制台、日誌、文字檔 | 文字檔、報表、國際化 | 二進制資料交換、序列化 |
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
public class PrintStreamDemo {
public static void main(String[] args) {
demoFileOutput();
demoFormatting();
demoErrorHandling();
}
// 多種建構方式輸出檔案
private static void demoFileOutput() {
byte[] data = {0x61, 0x62, 0x63, 0x64, 0x65}; // "abcde"
// 方式 1:包裝 FileOutputStream
try (PrintStream ps = new PrintStream(new FileOutputStream("ps1.txt"))) {
ps.write(data);
} catch (IOException e) { e.printStackTrace(); }
// 方式 2:直接指定 File
try (PrintStream ps = new PrintStream(new File("ps2.txt"))) {
ps.write(data);
} catch (IOException e) { e.printStackTrace(); }
// 方式 3:指定檔名與 UTF-8 編碼
try (PrintStream ps = new PrintStream("ps3.txt", StandardCharsets.UTF_8.name())) {
ps.write(data);
} catch (IOException e) { e.printStackTrace(); }
}
// 格式化輸出展示
private static void demoFormatting() {
try (PrintStream ps = new PrintStream("formatted.txt", StandardCharsets.UTF_8.name())) {
ps.println("=== PrintStream 格式化示範 ===");
ps.printf("整數: %d, 八進位: %o, 十六進位: 0x%X%n", 255, 255, 255);
ps.printf("浮點: %.4f, 科學記號: %e, 百分比: %%%n", Math.PI, Math.PI);
ps.format(Locale.TAIWAN, "本地化金額: %,.2f 元%n", 1234567.89);
ps.print("鏈式列印: ");
ps.print(true).print(" | ").print(42).print(" | ").println(3.14);
ps.append('[').append("APPEND").append(']').println();
} catch (IOException e) { e.printStackTrace(); }
}
// 錯誤處理模式
private static void demoErrorHandling() {
// 模擬寫入失敗:磁碟滿、權限不足等
PrintStream ps = new PrintStream(new FailingOutputStream());
ps.println("第一行");
ps.println("第二行");
ps.printf("數字: %d%n", 123);
if (ps.checkError()) {
System.err.println("PrintStream 發生寫入錯誤!trouble 旗標已置位");
}
ps.close(); // 關閉不拋出異常
}
// 故意失敗的 OutputStream
static class FailingOutputStream extends java.io.OutputStream {
private int count = 0;
@Override
public void write(int b) throws IOException {
if (++count > 10) throw new IOException("模擬磁碟寫入失敗");
}
}
}
// 捕獲標準輸出進行單元測試
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream originalOut = System.out;
try {
System.setOut(new PrintStream(baos, true, StandardCharsets.UTF_8));
// 執行被測代碼
System.out.println("測試輸出行 1");
System.out.printf("格式化: %d%n", 42);
} finally {
System.setOut(originalOut); // 必須恢復
}
String captured = baos.toString(StandardCharsets.UTF_8);
assert captured.contains("測試輸出行 1");
assert captured.contains("格式化: 42");
new PrintStream(file, StandardCharsets.UTF_8.name()) 避免平台編碼差異。println 或手動 flush()。print(0x61) 輸出 "97" 而非位元組 0x61,二進制請用 DataOutputStream 或裸 OutputStream。PrintStream 同步鎖在高併發下成為瓶頸,改用 Logback AsyncAppender。OutputStreamWriter 處理字元編碼,print() 系列本質是字串轉位元組。println/printf/format/換行符 觸發。clearError()(protected)。PrintStream 是 Java 位元組導向格式化輸出的基石,掌握其編碼鏈路、自動刷新語義、錯誤吞噬機制與格式化能力,能正確處理控制台輸出、檔案日誌、測試捕獲等核心場景。