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

Java IO 列印流:PrintStream 位元組列印輸出流完整解析

     編輯:Java 相關知識

核心定位:位元組導向的格式化列印流

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' 時觸發。

核心 API 分類


// 格式化輸出
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 vs PrintWriter vs DataOutputStream

特性PrintStreamPrintWriterDataOutputStream
資料單位位元組字元位元組
編碼指定建構子支援建構子支援固定 UTF-8
自動刷新支援支援不支援
異常處理吞噬,checkError()吞噬,checkError()拋出 IOException
格式化printf/formatprintf/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("模擬磁碟寫入失敗");
        }
    }
}

System.out 重定向與測試捕獲


// 捕獲標準輸出進行單元測試
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");

最佳實踐與避坑指南

  1. 生產環境明確指定 UTF-8:new PrintStream(file, StandardCharsets.UTF_8.name()) 避免平台編碼差異。
  2. 不要忽略 checkError():長時間運行的列印任務(如日誌輸出)定期檢查,避免靜默資料遺失。
  3. 區分 print/println 觸發刷新時機:需即時可見時用 println 或手動 flush()。
  4. 二進制資料勿用 PrintStream:print(0x61) 輸出 "97" 而非位元組 0x61,二進制請用 DataOutputStream 或裸 OutputStream。
  5. 高併發日誌考慮非同步框架:PrintStream 同步鎖在高併發下成為瓶頸,改用 Logback AsyncAppender。

常見誤區

  • 以為 PrintStream 只處理位元組:實際內部透過 OutputStreamWriter 處理字元編碼,print() 系列本質是字串轉位元組。
  • autoFlush 會在每個 print() 後刷新:僅 println/printf/format/換行符 觸發。
  • checkError() 會重置旗標:不會,需建立新實例或手動 clearError()(protected)。

PrintStream 是 Java 位元組導向格式化輸出的基石,掌握其編碼鏈路、自動刷新語義、錯誤吞噬機制與格式化能力,能正確處理控制台輸出、檔案日誌、測試捕獲等核心場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved