Java IO 採用**裝飾器模式**構建靈活流體系。本文通過兩個完整實戰案例——**目錄遞歸拷貝 .java 文件**與**文件行順序反轉**,串聯字節流、字符流、緩衝流、遞歸遍歷、異常處理、資源關閉等核心知識點。
| 維度 | 字節流 | 字符流 |
|---|---|---|
| 基礎抽象 | InputStream / OutputStream | Reader / Writer |
| 處理單位 | 字節 (byte/8bit) | 字符 (char/16bit UTF-16) |
| 適用場景 | 圖片、視頻、二進制文件、網絡傳輸 | 文本文件、配置文件、源代碼 |
| 核心實現 | FileInputStream, FileOutputStream | FileReader, FileWriter |
| 緩衝增強 | BufferedInputStream, BufferedOutputStream | BufferedReader, BufferedWriter |
| 轉換橋樑 | InputStreamReader / OutputStreamWriter (指定 Charset) | |
.java 後綴文件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);
}
} // 自動關閉流
}
}
File.listFiles() 遞歸更健壯。new byte[8192] 適配磁盤頁面大小,平衡記憶體與系統調用次數。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("行反轉完成!");
}
}
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)
}
}
}
| 任務 | 推薦 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) / walkFileTree | Stream 風格/訪問者模式 |
StandardCharsets.UTF_8 避免平台預設編碼不一致 (Windows GBK vs Linux UTF-8)。new BufferedInputStream(new FileInputStream(file)),順序不可反。RandomAccessFile / FileChannel 支持 seek、內存映射 (MappedByteBuffer)。ObjectInputStream/OutputStream 需實現 Serializable,注意 serialVersionUID 維護。IO 處理核心模式:**來源 → 裝飾器鏈 (緩衝/轉碼/壓縮/加密) → 目的地**。掌握 Files (NIO.2) 與 Stream API 可覆蓋 95% 場景;傳統 java.io 僅用於維護舊代碼或特殊需求 (如 RandomAccessFile、ObjectOutputStream)。永遠使用 **try-with-resources** 管理資源,指定 **UTF-8**,善用 **緩衝**。