java.io.File 是 Java 操作文件系統元數據(路徑、屬性、目錄結構)的入口類,**不直接讀寫文件內容**,而是為 I/O 流提供路徑抽象。掌握其構造模式、跨平台路徑處理、目錄遞歸遍歷、文件名過濾等核心能力,是文件操作的基本功。本文系統整理 5 大操作類別、路徑分隔符陷阱、NIO.2 現代化替代方向。
// 1. 完整路徑字符串 (最常用)
File f1 = new File("C:/data/logs/app.log"); // 推薦正斜杠,跨平台自動識別
File f2 = new File("C:\\data\\logs\\app.log"); // Windows 反斜杠需轉義
// 2. 父目錄 File + 子路徑字符串 (靈活拼接)
File parent = new File("C:/data/logs");
File f3 = new File(parent, "app.log"); // C:/data/logs/app.log
// 3. 父路徑字符串 + 子路徑字符串 (同模式 2)
File f4 = new File("C:/data/logs", "app.log"); // 同 f3
// ⚠️ 陷阱:File 不創建實際文件/目錄,僅封裝路徑信息
// 創建需調用 createNewFile() / mkdir() / mkdirs()
/:Java 內部自動轉換為平台分隔符(Windows \、Unix /)File.separator 或 Paths.get():避免硬編碼File.separator (\ 或 /)、File.pathSeparator (; 或 :)| 操作 | 方法 | 返回值 | 關鍵說明 |
|---|---|---|---|
| 創建文件 | createNewFile() | boolean | 文件存在返回 false;父目錄不存在拋 IOException |
| 創建目錄 | mkdir() | boolean | 單級目錄,父目錄必須存在 |
| 創建多級目錄 | mkdirs() | boolean | 遞歸創建父目錄,**常用** |
| 重命名/移動 | renameTo(File dest) | boolean | 跨分區/磁盤可能失敗;目標存在覆蓋行為平台相關 |
| 刪除 | delete() | boolean | 目錄必須為空;非空目錄需遞歸刪除子項 |
| JVM 退出刪除 | deleteOnExit() | void | 掛鉤 JVM 關閉鉤子,**臨時文件清理首選** |
| 判斷存在 | exists() | boolean | 文件/目錄均可 |
| 是否文件 | isFile() | boolean | |
| 是否目錄 | isDirectory() | boolean | |
| 是否隱藏 | isHidden() | boolean | 平台相關 (Windows 屬性、Unix 以 . 開頭) |
| 是否絕對路徑 | isAbsolute() | boolean |
File f = new File("C:/data/app.log");
if (f.exists()) {
f.getName(); // app.log
f.getPath(); // C:/data/app.log
f.getAbsolutePath(); // C:\data\app.log (平台規範化)
f.getParent(); // C:/data
f.length(); // 文件大小 (bytes),目錄返回 0
f.lastModified(); // 最後修改時間戳 (ms since epoch)
f.canRead(); / canWrite(); / canExecute(); // 權限檢查
// 時間戳格式化
LocalDateTime ldt = LocalDateTime.ofInstant(
Instant.ofEpochMilli(f.lastModified()), ZoneId.systemDefault());
System.out.println(ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
File dir = new File("C:/projects");
// String[] list() // 僅文件名,含隱藏文件
// String[] list(FilenameFilter filter) // 過濾
// File[] listFiles() // File 對象數組,**推薦**,含完整路徑
// File[] listFiles(FileFilter) // 文件/目錄過濾
// File[] listFiles(FilenameFilter) // 僅文件名過濾
totalSize += f.length());
name.endsWith(".java"));
// 或 File[] javaFiles = dir.listFiles((d, name) -> name.endsWith(".java")); // 返回 File[]
// 靈活過濾:正則匹配
FilenameFilter regexFilter = (d, name) -> name.matches(".*Test\\.java$");
六、 現代化替代:NIO.2 (java.nio.file) 為什麼更好?
| 場景 | File (Legacy) | Path + Files (NIO.2, JDK 7+) |
|---|---|---|
| 異常處理 | 返回 boolean,失敗原因不清 | 拋 IOException,含詳細錯誤信息 |
| 符號鏈接 | 不支持 | Files.isSymbolicLink(), Files.readSymbolicLink() |
| 文件屬性批量讀取 | 多次調用 | Files.readAttributes(path, "*") 一次性獲取 |
| 目錄流式遍歷 | 遞歸 listFiles 易 OOM | Files.walk() / Files.find() 流式,支持並行 |
| 文件複製/移動 | 手寫流複製 | Files.copy(), Files.move() 原子化、支持 REPLACE_EXISTING |
| 監聽文件變更 | 不支持 | WatchService 註冊目錄變更事件 |
Path path = Paths.get("C:/data/app.log");
// 創建多級目錄
Files.createDirectories(path.getParent());
// 讀取所有行 (流式,自動關閉)
List lines = Files.readAllLines(path, StandardCharsets.UTF_8);
// 寫入 (原子化:寫臨時文件再 rename)
Files.write(path, "content".getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
// 遞歸遍歷 (並行流)
Files.walk(Paths.get("C:/src"))
.filter(p -> p.toString().endsWith(".java"))
.parallel()
.forEach(System.out::println);
// 文件監聽
WatchService watcher = FileSystems.getDefault().newWatchService();
Paths.get("C:/data").register(watcher, StandardWatchEventKinds.ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
File 判斷文件是否為空:f.length() == 0,**目錄始終返回 0**renameTo 跨磁盤失效:Windows C:→D: 返回 false,**改用 Files.move()**delete() 非空目錄失敗:需先遞歸刪除子項,**或用 Files.walkFileTree() 刪除樹**"C:\\data" → Linux 失效,**統一用 / 或 Paths.get()**exists() 後再操作:TOCTOU 競態,**直接操作捕獲異常**listFiles() 返回巨大數組:OOM 風險,**改用 Files.list() Stream 流式處理**File 類作為 Java 文件系統抽象的基石,雖在 NIO.2 時代逐漸退居二線,但其路徑構造、屬性判斷、目錄遍歷邏輯仍是理解文件操作的核心。新項目**首選 Path/Files**,存量代碼掌握 File 邊界與避坑指南,實現平滑演進。