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

Java I/O 體系深度解析:從流、裝飾者模式到 NIO 基石

     編輯:Java 相關知識

一、輸入/輸出流的兩大家族

Java I/O 的核心抽象是 Stream(流)—— 代表資料源或資料目的地。所有流分為兩大體系:

  • 位元組流(Byte Stream):以 8-bit 位元組為單位,處理圖片、聲音、二進位檔案。基類為 InputStream/OutputStream。
  • 字元流(Character Stream):以 16-bit Unicode 字元為單位,處理文字檔案。基類為 Reader/Writer,內部自動處理編碼轉換。

1.1 位元組流主要實作

類型輸入流輸出流用途
記憶體緩衝ByteArrayInputStreamByteArrayOutputStream記憶體間資料傳遞
字串/字串緩衝StringBufferInputStream (已棄用)—舊版相容,勿用
檔案FileInputStreamFileOutputStream磁碟檔案讀寫
管道(執行緒間通訊)PipedInputStreamPipedOutputStream生產者-消費者模式
序列合併SequenceInputStream—將多輸入流串聯為一

1.2 字元流主要實作

字元流與位元組流一一對應,差異在於 編碼/解碼 由 InputStreamReader/OutputStreamWriter 自動完成(預設平台編碼或指定 Charset)。

位元組流對應字元流說明
ByteArrayInputStreamCharArrayReaderchar[] 為來源
FileInputStreamFileReader檔案文字讀取
PipedInputStreamPipedReader執行緒管道
ByteArrayOutputStreamCharArrayWriter寫入 char[]
FileOutputStreamFileWriter寫入文字檔
PipedOutputStreamPipedWriter執行緒管道

二、橋接兩大體系:InputStreamReader / OutputStreamWriter

// 位元組流 → 字元流(指定 UTF-8 編碼)
Reader  in  = new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8);
Writer  out = new OutputStreamWriter(new FileOutputStream("out.txt"), StandardCharsets.UTF_8);

這是處理中文等非 ASCII 文字檔的標準做法,避免平台預設編碼不一致導致亂碼。

三、裝飾者模式:為流動態添加功能

Java I/O 大量使用 Decorator Pattern,透過 FilterInputStream/FilterOutputStream 及其子類,在不修改原始流的前提下疊加功能:

功能位元組流裝飾類字元流裝飾類
緩衝(提升效能)BufferedInputStreamBufferedReader
基本型別讀寫DataInputStream— (用 DataInputStream 或 BufferedReader.readLine())
行號追蹤LineNumberInputStreamLineNumberReader
回推一字元/位元組PushbackInputStreamPushbackReader
格式化輸出PrintStreamPrintWriter

實戰範例:帶緩衝、格式化的檔案寫入

try (PrintWriter pw = new PrintWriter(
        new BufferedWriter(new FileWriter("log.txt", StandardCharsets.UTF_8)))) {
    pw.printf("Line %d: %s%n", 1, "Hello Decorator");
}

四、RandomAccessFile:隨機存取檔案

  • 同時實作 DataInput、DataOutput 介面,支援基本型別讀寫。
  • 建構子指定模式:"r" 唯讀、"rw" 讀寫、"rws"/"rwd" 同步寫入。
  • seek(long pos) 移動檔案指標,實現隨機讀寫(如資料庫索引、斷點續傳)。

五、綜合應用範例(TestIO.java 重構版)

import java.io.*;
import java.nio.charset.StandardCharsets;

public class ModernIOdemo {
    public static void main(String[] args) throws IOException {
        // 1. 帶緩衝逐行讀取檔案
        StringBuilder content = new StringBuilder();
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(new FileInputStream("src/TestIO.java"), StandardCharsets.UTF_8))) {
            String line;
            while ((line = br.readLine()) != null) content.append(line).append('\n');
        }

        // 2. 從鍵盤讀取一行(同樣用 BufferedReader 包裝 System.in)
        try (BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
            System.out.print("Enter a line: ");
            System.out.println(console.readLine());
        }

        // 3. 從字串讀取(StringReader)
        try (StringReader sr = new StringReader(content.toString())) {
            int c;
            while ((c = sr.read()) != -1) System.out.print((char)c);
        }

        // 4. 格式化寫入檔案(行號 + 內容)
        try (BufferedReader br = new BufferedReader(new StringReader(content.toString()));
             PrintWriter pw = new PrintWriter(new BufferedWriter(
                     new OutputStreamWriter(new FileOutputStream("TestIO.out"), StandardCharsets.UTF_8)))) {
            String line; int n = 1;
            while ((line = br.readLine()) != null) pw.println(n++ + ": " + line);
        }

        // 5. 基本型別序列化/反序列化(DataOutputStream/DataInputStream)
        try (DataOutputStream dos = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream("data.bin")))) {
            dos.writeDouble(Math.PI);
            dos.writeUTF("\nThis was pi: writeUTF\n");
            dos.writeBytes("This was pi: writeBytes\n");
        }
        try (DataInputStream dis = new DataInputStream(
                new BufferedInputStream(new FileInputStream("data.bin")))) {
            System.out.println(dis.readDouble());
            System.out.println(dis.readUTF());
            System.out.println(dis.readLine()); // 對應 writeBytes
        }

        // 6. RandomAccessFile 隨機寫入/讀取 double 陣列
        try (RandomAccessFile raf = new RandomAccessFile("rtest.dat", "rw")) {
            for (int i = 0; i < 10; i++) raf.writeDouble(i * 1.414);
            raf.seek(5 * 8); // 第 6 個 double (索引 5)
            raf.writeDouble(47.0001);
            raf.seek(0);
            for (int i = 0; i < 10; i++) System.out.println("Value " + i + ": " + raf.readDouble());
        }
    }
}

六、關鍵觀念回顧

  1. 流的方向:相對於程式(記憶體),Input = 讀入,Output = 寫出。
  2. 裝飾鏈順序:new Outer(new Middle(new Inner(source))),外層功能優先生效(如 BufferedReader 先緩衝,再由 FileReader 讀檔)。
  3. 編碼顯式化:所有跨平台文字 I/O 必須指定 StandardCharsets.UTF_8,切勿依賴預設值。
  4. 資源管理:JDK 7+ 必須使用 try-with-resources 確保流關閉,避免檔案鎖定與記憶體洩漏。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved