1. 檢查檔案與目錄是否存在的方法
處理檔案有時像是在地雷區散步:你永遠不知道下一個位元組後面會發生什麼。好消息是:Java 提供了「金屬探測器」——用來檢查檔案與資料夾是否存在的方法。
File 類別:透過 exists()、isFile()、isDirectory() 檢查
傳統做法——使用類別 java.io.File:
File file = new File("example.txt");
if (file.exists()) {
System.out.println("檔案存在!");
} else {
System.out.println("找不到檔案。");
}
方法 exists() 會在檔案或資料夾名稱存在時回傳 true。但這還不夠!有時你需要知道存在的是檔案還是目錄。
if (file.isFile()) {
System.out.println("這是檔案。");
} else if (file.isDirectory()) {
System.out.println("這是資料夾。");
} else {
System.out.println("未找到任何項目。");
}
Path 與 Files 類別:現代作法
在現代程式中,建議使用較新且更強大的 API java.nio.file。在這裡,用於檢查是否存在的是靜態方法 Files.exists():
import java.nio.file.*;
Path path = Paths.get("example.txt");
if (Files.exists(path)) {
System.out.println("透過 NIO 找到檔案!");
}
要檢查物件類型,請使用:
if (Files.isRegularFile(path)) {
System.out.println("這是一般檔案!");
}
if (Files.isDirectory(path)) {
System.out.println("這是資料夾!");
}
提示:對於新專案,建議直接使用 NIO(Path、Files),因為這個 API 較為現代、功能更完整,且與 try-with-resources 更加合拍。
表格:方法比較
| 方法 | 是否存在 | 類型檢查(檔案/資料夾) | 新舊程度 |
|---|---|---|---|
|
是 | 是(isFile(), isDirectory()) | 舊式 |
|
是 | 是(isRegularFile(), isDirectory()) | 建議使用 |
2. TOCTOU 問題:為什麼「檢查」不是萬靈丹
問題的本質是什麼?
假設你檢查過:「檔案存在!」——接著立刻決定讀取它。但在這兩個動作之間,以處理器的尺度來看可能已經過了很久。期間檔案可能被刪除、移動、被其他程序取代,或其存取權限被修改。
這就像打開冰箱,看到有蛋糕,關上門,然後再打開——發現蛋糕已經被吃掉了。程式設計中也有這樣的「室友」:其他程序、使用者、防毒軟體,甚至檔案系統本身。
為什麼這很重要?
因此,即使你確認檔案存在,在嘗試開啟它時仍可能拋出例外——例如 FileNotFoundException 或 AccessDeniedException。
所以檢查並非保證,只是額外的保險。務必隨時準備面對例外並妥善處理!
3. 實作:讀取前先檢查檔案是否存在
我們寫一個函式:若檔案存在就輸出其內容,若不存在則通知使用者。示範兩種作法:傳統 API 與新式 API。
作法 1:透過 File
import java.io.*;
public class FileExistenceCheck {
public static void main(String[] args) {
File file = new File("notes.txt");
if (file.exists() && file.isFile()) {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("讀取檔案時發生錯誤: " + e.getMessage());
}
} else {
System.out.println("找不到檔案 'notes.txt'。");
}
}
}
作法 2:透過 Path 與 Files
import java.nio.file.*;
import java.io.IOException;
public class PathExistenceCheck {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
if (Files.exists(path) && Files.isRegularFile(path)) {
try {
Files.lines(path).forEach(System.out::println);
} catch (IOException e) {
System.out.println("讀取檔案時發生錯誤: " + e.getMessage());
}
} else {
System.out.println("找不到檔案 'notes.txt'。");
}
}
}
即使檢查過——仍要捕捉例外!
在兩個範例中,即使事先做了檢查,我們仍然使用 try–catch 區塊,因為檔案可能隨時消失或變得不可存取。這是操作檔案的黃金法則!
4. 檢查資料夾是否存在
同樣地可以檢查資料夾的存在;若不存在就建立:
import java.nio.file.*;
public class DirectoryCheck {
public static void main(String[] args) {
Path dir = Paths.get("data");
if (Files.exists(dir) && Files.isDirectory(dir)) {
System.out.println("找到資料夾 'data'。");
} else {
System.out.println("找不到資料夾 'data'。正在建立…");
try {
Files.createDirectory(dir);
System.out.println("已建立資料夾!");
} catch (IOException e) {
System.out.println("建立資料夾時發生錯誤: " + e.getMessage());
}
}
}
}
順帶一提:
方法 Files.createDirectory() 會在資料夾已存在時丟出例外。若想建立一串巢狀資料夾(例如 "data/2025/09"),請使用 Files.createDirectories();即使部份資料夾已存在也不會報錯。
5. 重點與細節:相對與絕對路徑
相對路徑
當你寫下 "notes.txt" 時,程式會在「目前工作目錄」中尋找檔案。這個目錄的位置取決於你如何、從哪裡啟動應用程式(IDE、終端機、雙擊 JAR 等等)。
絕對路徑
若要確定尋找的位置,最好使用絕對路徑或動態建構路徑:
String userHome = System.getProperty("user.home");
Path filePath = Paths.get(userHome, "myapp", "notes.txt");
檢查物件類型
有時「檔案」可能意外是個資料夾。因此不僅要檢查是否存在,還要檢查類型:
if (Files.isRegularFile(path)) {
// 這確實是檔案!
}
if (Files.isDirectory(path)) {
// 這是資料夾!
}
6. TOCTOU 問題的實際示範
模擬一個情境:在檢查之後、開啟之前檔案消失。執行程式,並在檢查與讀取之間手動刪除檔案:
import java.io.*;
import java.nio.file.*;
public class TOCTOUExample {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
if (Files.exists(path)) {
System.out.println("找到檔案,準備開始讀取…");
// 在此時請打開檔案總管並手動刪除 "notes.txt"!
try {
Files.lines(path).forEach(System.out::println);
} catch (IOException e) {
System.out.println("糟了!檔案不見了: " + e.getMessage());
}
} else {
System.out.println("找不到檔案。");
}
}
}
結果:
如果你能在檢查與讀取之間把檔案刪掉,就會收到例外。這清楚展示了:即使很小心地先檢查,也無法避免不可預期的變化。
7. 檢查檔案與資料夾存在時的常見錯誤
錯誤 №1:只依賴檢查,不使用 try–catch。 許多新手會想:既然「檔案在」,那就可以放心讀取。但檔案可能會消失或變得不可存取,程式就會「崩潰」。
錯誤 №2:只檢查存在,不檢查類型。 如果你只檢查 exists() 而不確認是檔案還是資料夾,可能會把資料夾當作檔案去開啟——結果出錯。
錯誤 №3:不理解工作目錄就使用相對路徑。 程式在「不對的地方」找檔案,使用者就不明白為什麼什麼都沒有發生。
錯誤 №4:忽略存取權限。 檔案或資料夾可能存在,但沒有讀寫權限。這類錯誤只會在嘗試開啟時暴露——務必使用 try–catch。
錯誤 №5:忽略不同作業系統的大小寫規則。 在 Windows,檔名不區分大小寫;在 Linux,則區分大小寫。如果你找 "notes.txt",程式可能找不到 "Notes.txt"。
GO TO FULL VERSION