自 Java 7 發布以來,java.nio.file 套件引入了全新的文件 I/O API,徹底改變了開發者處理文件系統的方式。傳統的 java.io.File 類雖然功能完整,但 API 設計較為笨拙,缺乏流暢的鏈式操作,且錯誤處理機制不夠完善。新引入的 Path、Paths 與 Files 三大核心類,構建了更現代、更強大、更符合直覺的文件操作體系。
File 成為路徑操作的核心介面,提供豐富的路徑解析、組合、相對化等功能。get() 快速構建 Path 實例,支援字串路徑與 URI 兩種輸入來源。// 1. 透過 Paths 工具類(最常用)
Path path1 = Paths.get("/Users/kingboy/Desktop/");
Path path2 = Paths.get(URI.create("file:///Users/kingboy/Desktop/"));
// 2. 透過 FileSystems(可指定特定文件系統)
Path path3 = FileSystems.getDefault().getPath("/Users/kingboy/Desktop/");
// 3. 從舊版 File 物件轉換(兼容舊代碼)
Path path4 = new File("/Users/kingboy/Desktop/").toPath();
建議優先使用 Paths.get(),代碼最簡潔且可讀性最強。
// 創建單層目錄(父目錄必須存在)
Path dir = Paths.get("/tmp/demo");
if (Files.notExists(dir)) {
Files.createDirectory(dir);
}
// 創建多層目錄(自動創建父目錄)
Files.createDirectories(Paths.get("/tmp/a/b/c"));
// 創建空文件(若已存在則拋出異常)
Path file = Paths.get("/tmp/demo.txt");
if (Files.notExists(file)) {
Files.createFile(file);
}
關鍵差異:createDirectory 僅創建最後一層,createDirectories 會遞歸創建所有不存在的父目錄。
Path path = Paths.get("/tmp/test.txt");
// 基本屬性
FileTime modifiedTime = Files.getLastModifiedTime(path);
UserPrincipal owner = Files.getOwner(path);
long size = Files.size(path);
// POSIX 權限(類 Unix 系統)
Set<PosixFilePermission> perms = Files.getPosixFilePermissions(path);
// 輸出類似:[OWNER_READ, OWNER_WRITE, GROUP_READ, OTHERS_READ]
// 設置權限(需類 Unix 環境)
Set<PosixFilePermission> newPerms = PosixFilePermissions.fromString("rw-r--r--");
Files.setPosixFilePermissions(path, newPerms);
Path path = Paths.get("/tmp/data.txt");
// 方式 1:BufferedReader 流式讀取(大文件推薦)
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// 方式 2:一次性讀取所有行(小文件適用)
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
lines.forEach(System.out::println);
// 方式 3:Java 8+ Stream 流式處理(記憶體友好)
try (Stream<String> stream = Files.lines(path, StandardCharsets.UTF_8)) {
stream.filter(l -> l.contains("ERROR"))
.forEach(System.out::println);
}
Path path = Paths.get("/tmp/output.txt");
String content = "Hello, NIO.2!";
// 方式 1:BufferedWriter
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
writer.write(content);
}
// 方式 2:Files.write 便捷方法(Java 8+)
Files.write(path, content.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
// 方式 3:寫入行集合
List<String> lines = Arrays.asList("Line 1", "Line 2", "Line 3");
Files.write(path, lines, StandardCharsets.UTF_8);
// 輸入流
try (InputStream in = Files.newInputStream(path)) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
// 處理數據
}
}
// 輸出流
try (OutputStream out = Files.newOutputStream(path,
StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
out.write("Append data".getBytes());
}
Path dir = Paths.get("/tmp");
try (Stream<Path> stream = Files.list(dir)) {
stream.map(Path::getFileName)
.forEach(System.out::println);
}
Path root = Paths.get("/tmp");
// 方式 1:Files.walk(返回 Stream,支援深度控制)
try (Stream<Path> stream = Files.walk(root, 3)) { // 最大深度 3
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
// 方式 2:Files.walkFileTree(訪問者模式,控制力最強)
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".log")) {
System.out.println("Found log: " + file);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.println("Access denied: " + file);
return FileVisitResult.CONTINUE;
}
});
Path source = Paths.get("/tmp/source.txt");
Path target = Paths.get("/tmp/target.txt");
Path backup = Paths.get("/tmp/backup.txt");
// 複製(支援覆蓋、保留屬性等選項)
Files.copy(source, target,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
// 移動/重命名(同一文件系統內為原子操作)
Files.move(target, backup, StandardCopyOption.ATOMIC_MOVE);
// 刪除(不存在時拋出異常)
Files.delete(backup);
// 刪除(不存在時靜默返回 false)
boolean deleted = Files.deleteIfExists(Paths.get("/tmp/nonexistent"));
newBufferedReader、newInputStream、walk 等返回的 Stream)都實現了 AutoCloseable,務必用 try-with-resources 確保資源釋放。StandardCharsets.UTF_8,避免平台預設編碼導致的亂碼問題。NoSuchFileException、AccessDeniedException、FileAlreadyExistsException 等具體子類,而非通用的 IOException,便於精準業務邏輯處理。resolve()、relativize()、getParent()、getFileName() 等方法比字串拼接更安全、跨平台。Files.lines()、Files.walk() 返回惰性 Stream,配合 filter、map 流式處理,避免一次性載入記憶體導致 OOM。Java 7 NIO.2 的 Path、Paths、Files 三劍客,以現代化的 API 設計、豐富的功能覆蓋、優異的效能表現,全方位超越了舊版 File 類。掌握這套 API,不僅能編寫出更簡潔、健壯、高效的文件操作代碼,更能輕鬆應對複雜的文件系統交互場景。建議在所有新項目中全面採用 NIO.2 API,舊代碼也可漸進式遷移。