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

Java IO 流完全指南:分類體系、核心 API、讀寫實戰與避坑大全

     編輯:Java 程式設計入門

IO 流四大分類維度

分類維度類別代表類核心特徵
按數據類型字節流InputStream / OutputStream處理二進制:圖片、視頻、音頻、序列化對象
字符流Reader / Writer處理文本:txt、JSON、XML、CSV (內部按 char[] 處理)
按流向輸入流InputStream / Reader設備 → 內存 (read)
輸出流OutputStream / Writer內存 → 設備 (write)
按封裝層級節點流/原始流FileInputStream, FileReader直接連接數據源,構造器不接受其他流
處理流/包裝流BufferedInputStream, InputStreamReader包裝其他流,提供緩衝、轉碼、對象化等增強功能
按功能基礎流File, ByteArray, Piped直接操作具體設備/內存
工具流Data, Object, Print, Buffered提供特定數據類型讀寫、緩衝、格式化等能力

核心類繼承結構速覽


java.io.InputStream (抽象)
├── FileInputStream          // 文件字節輸入
├── ByteArrayInputStream     // 內存字節數組輸入
├── FilterInputStream        // 過濾器基類
│   ├── BufferedInputStream  // 緩衝輸入 (核心:8KB 默認緩衝區)
│   ├── DataInputStream      // 基本類型讀取 (readInt, readUTF...)
│   └── ObjectInputStream    // 對象反序列化
├── PipedInputStream         // 管道輸入
└── SequenceInputStream      // 串聯多個輸入流

java.io.Reader (抽象)
├── FileReader               // 文件字符輸入 (默認平台編碼)
├── InputStreamReader        // 字節→字符轉換橋樑 (關鍵:指定編碼)
│   └── FileReader 實際繼承此類
├── BufferedReader           // 緩衝字符輸入 (核心:readLine())
└── CharArrayReader          // 字符數組輸入

OutputStream / Writer 體系對稱...

實戰代碼大全

1. 字節流:圖片複製 (最基礎)


public static void copyFileBytes(String src, String dest) throws IOException {
    try (InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dest)) {
        
        byte[] buffer = new byte[8192];  // 8KB 緩衝區
        int len;
        while ((len = in.read(buffer)) != -1) {
            out.write(buffer, 0, len);
        }
    }
}

2. 字符流:文本文件讀取 (指定編碼)


// 方式一:Files.readString (Java 11+, 最簡潔)
String content = Files.readString(Paths.get("file.txt"), StandardCharsets.UTF_8);

// 方式二:BufferedReader 逐行讀取 (經典,大文件友好)
public static List readLines(String path, String charset) throws IOException {
    List lines = new ArrayList<>();
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(new FileInputStream(path), charset))) {
        String line;
        while ((line = reader.readLine()) != null) {
            lines.add(line);
        }
    }
    return lines;
}

// 方式三:Scanner (簡單解析)
try (Scanner scanner = new Scanner(new File("data.txt"), "UTF-8")) {
    while (scanner.hasNextLine()) {
        System.out.println(scanner.nextLine());
    }
}

3. 緩衝流:高效讀寫標配


// 字節緩衡 - 複制大文件
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
     BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest))) {
    byte[] buf = new byte[16384];
    int len;
    while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
}

// 字符緩衡 - 逐行處理
try (BufferedReader br = new BufferedReader(new FileReader("log.txt"));
     BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        bw.write(processLine(line));
        bw.newLine();  // 跨平台換行
    }
}

4. 轉換流:編碼控制關鍵


// 讀取 GBK 文件
try (Reader reader = new InputStreamReader(new FileInputStream("gbk.txt"), "GBK")) {
    // ...
}

// 寫入 UTF-8 文件
try (Writer writer = new OutputStreamWriter(new FileOutputStream("utf8.txt"), "UTF-8")) {
    // ...
}

// 系統默認編碼陷阱
// FileReader / FileWriter 使用平台默認編碼,跨平台會亂碼
// 強烈建議:顯式使用 InputStreamReader/OutputStreamWriter 指定編碼

5. 數據流:基本類型序列化


// 寫入
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.bin"))) {
    dos.writeInt(100);
    dos.writeDouble(3.14);
    dos.writeUTF("Hello");
    dos.writeBoolean(true);
}

// 讀取 (順序必須一致)
try (DataInputStream dis = new DataInputStream(new FileInputStream("data.bin"))) {
    int i = dis.readInt();
    double d = dis.readDouble();
    String s = dis.readUTF();
    boolean b = dis.readBoolean();
}

6. 對象流:對象序列化/反序列化


// 實現 Serializable 接口
class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private transient String password;  // transient 不序列化
    // getter/setter...
}

// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.obj"))) {
    oos.writeObject(new User("admin", "secret"));
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.obj"))) {
    User user = (User) ois.readObject();
}

7. 打印流:格式化輸出


try (PrintWriter pw = new PrintWriter(new FileWriter("report.txt"))) {
    pw.printf("用戶: %s, 年齡: %d, 薪資: %.2f%n", "張三", 25, 8500.0);
    pw.println("--- 分割線 ---");
    pw.format(Locale.CHINA, "金額: %,.2f 元%n", 1234567.89);
}

8. 內存流:高速臨時存儲


// 字節數組流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 寫入數據...
byte[] data = baos.toByteArray();

// 字符數組流
CharArrayWriter caw = new CharArrayWriter();
// 寫入字符...
String str = caw.toString();

// 字符串流 (僅讀)
try (Reader reader = new StringReader("line1\nline2")) {
    // ...
}

9. 文件複製進階:NIO.2 (Java 7+)


// 簡單複製
Files.copy(Paths.get(src), Paths.get(dest), StandardCopyOption.REPLACE_EXISTING);

// 帶進度回調
Files.copy(Paths.get(src), Paths.get(dest), 
    StandardCopyOption.REPLACE_EXISTING,
    (bytesCopied, totalBytes) -> System.out.printf("進度: %.1f%%%n", bytesCopied*100.0/totalBytes));

// 移動/重命名 (原子操作)
Files.move(Paths.get(src), Paths.get(dest), StandardCopyOption.ATOMIC_MOVE);

經典坑點與避坑指南

❌ 坑 1:忘記關閉流 → 文件句柄洩漏


// 錯誤
FileInputStream in = new FileInputStream("a.txt");
// 異常拋出時流未關閉

// 正確:try-with-resources (Java 7+)
try (FileInputStream in = new FileInputStream("a.txt")) {
    // 自動關閉,即使拋異常
}

❌ 坑 2:FileReader/FileWriter 編碼不顯式


// 危險:Windows(GBK) 寫入,Linux(UTF-8) 讀取 → 亂碼
new FileWriter("a.txt").write("中文");

// 安全:顯式指定 UTF-8
new OutputStreamWriter(new FileOutputStream("a.txt"), StandardCharsets.UTF_8).write("中文");

❌ 坑 3:read() 返回值誤判


// 錯誤:以 0 為結束標誌
while ((b = in.read()) != 0) { ... }  // 0 是合法字節!

// 正確:-1 表示結束
while ((b = in.read()) != -1) { ... }

❌ 坑 4:緩衝區未刷新導致數據丟失


BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
bw.write("重要數據");
// 程序異常退出,數據仍在緩衝區,文件為空!

// 正確:關閉流自動 flush,或關鍵點手動 flush
bw.flush();

❌ 坑 5:大文件 readAllBytes 導致 OOM


// 危險:一次性讀入內存
byte[] all = Files.readAllBytes(Paths.get("10GB.log"));

// 安全:流式處理
Files.lines(Paths.get("10GB.log")).forEach(line -> process(line));

❌ 坑 6:序列化 serialVersionUID 缺失


// 修改類結構後反序列化舊數據拋 InvalidClassException
// 必須顯式聲明
private static final long serialVersionUID = 1L;

性能優化清單

  1. 緩衝區大小:8KB-64KB (磁盤頁大小倍數),new BufferedInputStream(in, 65536)
  2. NIO 通道:FileChannel.transferTo() 零拷貝,大文件傳輸最快
  3. 直接緩衝區:ByteBuffer.allocateDirect() 避免堆外/堆內拷貝
  4. 異步 IO:AsynchronousFileChannel (Java 7+) 高併發場景
  5. 內存映射:FileChannel.map() 隨機訪問大文件

總結

Java IO 體系雖龐大,但掌握 字節/字符雙軌、裝飾器模式包裝增強、try-with-resources 自動資源管理 三大核心,即可應對 95% 場景。現代開發首選 java.nio.file.Files 靜態工具類簡化常見操作,複雜場景組合 BufferedInputStream/Reader + 顯式編碼。記住:流用完必關,編碼必顯式,大文件必流式。

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