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

Java IO 流實戰大全:字節/字符流、緩衝、遞歸拷貝、文件讀寫反轉完整案例

     編輯:Java 綜合教學

Java IO 體系實戰指南:從流分類到生產級文件處理模式

Java IO 採用**裝飾器模式**構建靈活流體系。本文通過兩個完整實戰案例——**目錄遞歸拷貝 .java 文件**與**文件行順序反轉**,串聯字節流、字符流、緩衝流、遞歸遍歷、異常處理、資源關閉等核心知識點。

一、IO 流體系速覽

維度字節流字符流
基礎抽象InputStream / OutputStreamReader / Writer
處理單位字節 (byte/8bit)字符 (char/16bit UTF-16)
適用場景圖片、視頻、二進制文件、網絡傳輸文本文件、配置文件、源代碼
核心實現FileInputStream, FileOutputStreamFileReader, FileWriter
緩衝增強BufferedInputStream, BufferedOutputStreamBufferedReader, BufferedWriter
轉換橋樑InputStreamReader / OutputStreamWriter (指定 Charset)

二、案例一:目錄遞歸拷貝 .java 文件 (字節流 + 緩衝)

需求

  • 鍵盤輸入源目錄與目標目錄
  • 遞歸遍歷源目錄,僅拷貝 .java 後綴文件
  • 保持目錄結構,子目錄自動創建
  • 使用緩衝流提升效能,正確關閉資源

完整代碼 (JDK 7+ try-with-resources)

import java.io.*;
import java.nio.file.*;
import java.util.Scanner;

public class JavaFileCopier {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        Path srcDir = getValidDirectory(scanner, "請輸入源目錄路徑: ");
        Path destDir = getValidDirectory(scanner, "請輸入目標目錄路徑: ");
        scanner.close();
        
        copyJavaFiles(srcDir, destDir);
        System.out.println("拷貝完成!");
    }
    
    // 獲取合法目錄路徑
    private static Path getValidDirectory(Scanner scanner, String prompt) {
        while (true) {
            System.out.print(prompt);
            String input = scanner.nextLine().trim();
            Path path = Paths.get(input);
            if (Files.notExists(path)) {
                System.out.println("路徑不存在,請重新輸入。");
            } else if (!Files.isDirectory(path)) {
                System.out.println("輸入非目錄,請重新輸入。");
            } else {
                return path.toAbsolutePath().normalize();
            }
        }
    }
    
    // 遞歸拷貝入口
    private static void copyJavaFiles(Path src, Path dest) throws IOException {
        Files.walkFileTree(src, new SimpleFileVisitor() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (file.toString().endsWith(".java")) {
                    Path target = dest.resolve(src.relativize(file));
                    Files.createDirectories(target.getParent()); // 確保父目錄存在
                    copyFileWithBuffer(file, target);
                }
                return FileVisitResult.CONTINUE;
            }
            
            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {
                System.err.println("無法訪問: " + file + " - " + exc.getMessage());
                return FileVisitResult.CONTINUE;
            }
        });
    }
    
    // 緩衝字節流拷貝 (8KB 緩衝區)
    private static void copyFileWithBuffer(Path src, Path dest) throws IOException {
        try (InputStream in = new BufferedInputStream(Files.newInputStream(src));
             OutputStream out = new BufferedOutputStream(Files.newOutputStream(dest))) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        } // 自動關閉流
    }
}

關鍵點解析

  1. Files.walkFileTree:NIO.2 遞歸遍歷標準 API,自動處理符號鏈接、權限異常,比手寫 File.listFiles() 遞歸更健壯。
  2. src.relativize(file):計算相對路徑,保持目錄結構映射到目標目錄。
  3. 8KB 緩衝區:new byte[8192] 適配磁盤頁面大小,平衡記憶體與系統調用次數。
  4. try-with-resources:JDK 7+ 自動資源管理,彈出異常也能保證關閉,嚴禁手動 close()。

三、案例二:文件行順序反轉 (字符流 + 緩衝)

需求

  • 讀取文本文件所有行
  • 首尾行交換、次首尾交換... (整體反轉)
  • 寫入新文件,保持換行符

完整代碼

public class LineReverser {
    public static void main(String[] args) throws IOException {
        Path input = Paths.get("abc/b.txt");
        Path output = Paths.get("abc/c.txt");
        
        // 1. 讀取所有行到 List (自動處理換行符)
        List lines = Files.readAllLines(input, StandardCharsets.UTF_8);
        
        // 2. 就地反轉 (Collections.reverse 底層雙指針交換 O(n/2))
        Collections.reverse(lines);
        
        // 3. 寫入 (Files.write 默認換行符為系統預設,指定 UTF-8)
        Files.write(output, lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        
        System.out.println("行反轉完成!");
    }
}

手寫流版本 (教學用,展示 BufferedReader/Writer)

public static void reverseWithStreams(Path src, Path dest) throws IOException {
    List lines = new ArrayList<>();
    
    // 讀取
    try (BufferedReader br = Files.newBufferedReader(src, StandardCharsets.UTF_8)) {
        String line;
        while ((line = br.readLine()) != null) { // readLine 去除換行符
            lines.add(line);
        }
    }
    
    // 反轉
    Collections.reverse(lines);
    
    // 寫入
    try (BufferedWriter bw = Files.newBufferedWriter(dest, StandardCharsets.UTF_8)) {
        for (String line : lines) {
            bw.write(line);
            bw.newLine(); // 寫入系統預設換行符 (\n/\r\n)
        }
    }
}

四、現代 Java (NIO.2 + Stream API) 最佳實踐

任務推薦 API優勢
讀取全文本Files.readString(path) (Java 11+)一行代碼,自動 UTF-8
讀取所有行Files.readAllLines(path)返回 List,自動關閉
大文件逐行處理Files.lines(path) → Stream惰性流,記憶體常數,try-with-resources 管理
寫入文本Files.writeString(path, content) (Java 11+)原子寫入選項,簡潔
拷貝文件/目錄Files.copy(src, dest, REPLACE_EXISTING)底層零拷貝/系統調用優化
遍歷目錄Files.walk(path) / walkFileTreeStream 風格/訪問者模式

五、避坑指南

  • 字符流只能處理文本:用 FileReader 讀圖片會損壞數據 (編碼轉換導致字節改變)。
  • 指定 Charset:StandardCharsets.UTF_8 避免平台預設編碼不一致 (Windows GBK vs Linux UTF-8)。
  • 緩衝流是裝飾器:new BufferedInputStream(new FileInputStream(file)),順序不可反。
  • 流關閉順序:外層關閉會級聯關閉內層,try-with-resources 自動按逆序關閉。
  • 隨機存取:RandomAccessFile / FileChannel 支持 seek、內存映射 (MappedByteBuffer)。
  • 序列化:ObjectInputStream/OutputStream 需實現 Serializable,注意 serialVersionUID 維護。

六、總結

IO 處理核心模式:**來源 → 裝飾器鏈 (緩衝/轉碼/壓縮/加密) → 目的地**。掌握 Files (NIO.2) 與 Stream API 可覆蓋 95% 場景;傳統 java.io 僅用於維護舊代碼或特殊需求 (如 RandomAccessFile、ObjectOutputStream)。永遠使用 **try-with-resources** 管理資源,指定 **UTF-8**,善用 **緩衝**。

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