Java 檔案操作
Java 提供多種方式操作檔案和目錄,包括傳統的 java.io.File 和較新的 java.nio.file API。
File 類別
java.io.File 是操作檔案和目錄的基本類別:
import java.io.File;
// 建立 File 物件(不會真的建立檔案)
File file = new File("test.txt");
File file2 = new File("/Users/user/documents/test.txt");
File file3 = new File("documents", "test.txt");
基本操作
檢查檔案/目錄
File file = new File("test.txt");
// 檢查存在
boolean exists = file.exists();
// 檢查類型
boolean isFile = file.isFile();
boolean isDir = file.isDirectory();
// 檢查權限
boolean canRead = file.canRead();
boolean canWrite = file.canWrite();
boolean canExecute = file.canExecute();
// 檢查是否隱藏
boolean hidden = file.isHidden();
取得檔案資訊
File file = new File("test.txt");
// 檔案名稱
String name = file.getName(); // "test.txt"
// 路徑
String path = file.getPath(); // 相對或絕對路徑
String absPath = file.getAbsolutePath(); // 絕對路徑
String canPath = file.getCanonicalPath();// 標準化路徑
// 父目錄
String parent = file.getParent();
File parentFile = file.getParentFile();
// 檔案大小(位元組)
long size = file.length();
// 最後修改時間
long lastModified = file.lastModified();
建立檔案和目錄
// 建立新檔案
File file = new File("newfile.txt");
boolean created = file.createNewFile(); // 如果不存在則建立
// 建立目錄
File dir = new File("newdir");
boolean dirCreated = dir.mkdir(); // 建立單層目錄
// 建立多層目錄
File dirs = new File("path/to/newdir");
boolean dirsCreated = dirs.mkdirs(); // 建立所有必要的父目錄
刪除和重新命名
// 刪除
File file = new File("test.txt");
boolean deleted = file.delete();
// 在 JVM 結束時刪除
file.deleteOnExit();
// 重新命名/移動
File oldFile = new File("old.txt");
File newFile = new File("new.txt");
boolean renamed = oldFile.renameTo(newFile);
列出目錄內容
File dir = new File(".");
// 列出檔案名稱
String[] names = dir.list();
// 列出 File 物件
File[] files = dir.listFiles();
// 使用過濾器
File[] txtFiles = dir.listFiles((d, name) -> name.endsWith(".txt"));
File[] dirs = dir.listFiles(File::isDirectory);
// 遍歷
for (File f : dir.listFiles()) {
System.out.println(f.getName() + " - " +
(f.isDirectory() ? "目錄" : "檔案"));
}
NIO.2 Path 和 Files
Java 7 引入的新 API,功能更強大:
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
// 建立 Path
Path path = Paths.get("test.txt");
Path path2 = Paths.get("/Users", "user", "documents", "test.txt");
Path path3 = Path.of("test.txt"); // Java 11+
檢查和取得資訊
Path path = Paths.get("test.txt");
// 檢查
boolean exists = Files.exists(path);
boolean isFile = Files.isRegularFile(path);
boolean isDir = Files.isDirectory(path);
boolean isReadable = Files.isReadable(path);
boolean isWritable = Files.isWritable(path);
// 檔案大小
long size = Files.size(path);
// 最後修改時間
FileTime lastModified = Files.getLastModifiedTime(path);
建立檔案和目錄
// 建立檔案
Path file = Files.createFile(Paths.get("newfile.txt"));
// 建立目錄
Path dir = Files.createDirectory(Paths.get("newdir"));
// 建立多層目錄
Path dirs = Files.createDirectories(Paths.get("path/to/newdir"));
// 建立暫存檔案
Path tempFile = Files.createTempFile("prefix", ".txt");
Path tempDir = Files.createTempDirectory("tempdir");
複製和移動
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
// 複製
Files.copy(source, target);
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// 移動
Files.move(source, target);
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
刪除
Path path = Paths.get("test.txt");
// 刪除(如果不存在會拋出例外)
Files.delete(path);
// 如果存在才刪除
boolean deleted = Files.deleteIfExists(path);
遍歷目錄
Path dir = Paths.get(".");
// 列出目錄內容
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path entry : stream) {
System.out.println(entry.getFileName());
}
}
// 使用 glob 模式過濾
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) {
for (Path entry : stream) {
System.out.println(entry);
}
}
// 遞迴遍歷
Files.walk(dir)
.filter(Files::isRegularFile)
.forEach(System.out::println);
// 限制深度
Files.walk(dir, 2)
.forEach(System.out::println);
實際範例
取得目錄大小
public long getDirectorySize(Path dir) throws IOException {
return Files.walk(dir)
.filter(Files::isRegularFile)
.mapToLong(p -> {
try {
return Files.size(p);
} catch (IOException e) {
return 0;
}
})
.sum();
}
尋找檔案
public List<Path> findFiles(Path dir, String extension) throws IOException {
return Files.walk(dir)
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(extension))
.collect(Collectors.toList());
}
// 使用
List<Path> javaFiles = findFiles(Paths.get("."), ".java");
複製目錄
public void copyDirectory(Path source, Path target) throws IOException {
Files.walk(source).forEach(s -> {
try {
Path t = target.resolve(source.relativize(s));
if (Files.isDirectory(s)) {
Files.createDirectories(t);
} else {
Files.copy(s, t, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
刪除目錄(包含內容)
public void deleteDirectory(Path dir) throws IOException {
Files.walk(dir)
.sorted(Comparator.reverseOrder()) // 先刪除檔案,再刪除目錄
.forEach(p -> {
try {
Files.delete(p);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
監控目錄變化
public void watchDirectory(Path dir) throws IOException, InterruptedException {
WatchService watchService = FileSystems.getDefault().newWatchService();
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path filename = (Path) event.context();
System.out.println(kind + ": " + filename);
}
key.reset();
}
}
File vs Path
| 特性 | File | Path |
|---|---|---|
| 套件 | java.io | java.nio.file |
| 版本 | Java 1.0 | Java 7 |
| 不可變性 | 可變 | 不可變 |
| 例外處理 | 回傳 boolean | 拋出例外 |
| 功能 | 基本 | 豐富 |
| 建議 | 舊程式碼 | 新程式碼 |
// 轉換
File file = new File("test.txt");
Path path = file.toPath();
Path path2 = Paths.get("test.txt");
File file2 = path2.toFile();
重點整理
File是傳統 API,Path/Files是較新的 NIO.2 API- 新程式碼建議使用
Path和Files Files.walk()方便遞迴遍歷目錄- 注意關閉 Stream 和 DirectoryStream(使用 try-with-resources)
WatchService可以監控目錄變化