NIO.2 文件操作(Path/Files)
面试提问
"Java NIO.2 的 Path 和 Files 类有什么用?与传统 IO 有什么区别?"
NIO.2 简介
Java 7 引入 NIO.2(JSR 203),提供了更好的文件系统 API:
| 类 | 作用 |
|---|---|
Path | 文件路径抽象 |
Files | 文件操作工具类 |
FileSystems | 文件系统工厂 |
WatchService | 文件监控服务 |
Path 接口
创建 Path
// 方式一:Paths 工具类
Path path1 = Paths.get("C:/temp/test.txt");
Path path2 = Paths.get("C:", "temp", "test.txt");
// 方式二:FileSystems
Path path3 = FileSystems.getDefault().getPath("C:/temp/test.txt");
// 方式三:File 转 Path
File file = new File("C:/temp/test.txt");
Path path4 = file.toPath();
// 方式四:Path 转 File
File file2 = path4.toFile();Path 常用方法
Path path = Paths.get("C:/temp/subdir/test.txt");
path.getFileName(); // test.txt
path.getParent(); // C:/temp/subdir
path.getRoot(); // C:/
path.getNameCount(); // 3 (temp, subdir, test.txt)
path.getName(0); // temp
path.subpath(0, 2); // temp/subdir
// 路径操作
path.resolve("other.txt"); // C:/temp/subdir/test.txt/other.txt
path.resolveSibling("other.txt"); // C:/temp/subdir/other.txt
path.relativize(Paths.get("C:/temp")); // ..\..
path.normalize(); // 去除冗余的 . 和 ..
path.toAbsolutePath(); // 转绝对路径Path vs File
| 特性 | File | Path |
|---|---|---|
| 引入版本 | JDK 1.0 | JDK 7 |
| 异常处理 | 返回 boolean | 抛出异常 |
| 符号链接 | 不支持 | 支持 |
| 文件属性 | 不支持 | 支持 |
| 性能 | 较差 | 更好 |
Files 工具类
文件操作
Path path = Paths.get("test.txt");
// 创建文件
Files.createFile(path);
// 创建目录
Files.createDirectory(Paths.get("dir"));
Files.createDirectories(Paths.get("a/b/c")); // 创建多级目录
// 删除文件/目录
Files.delete(path); // 不存在抛异常
Files.deleteIfExists(path); // 不存在返回 false
// 复制文件
Files.copy(source, target);
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// 移动文件
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
// 判断存在
Files.exists(path);
Files.notExists(path);
// 判断类型
Files.isDirectory(path);
Files.isRegularFile(path);
Files.isSymbolicLink(path);
// 获取文件大小
long size = Files.size(path);文件读写
Path path = Paths.get("test.txt");
// 读取所有字节
byte[] bytes = Files.readAllBytes(path);
// 读取所有行
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
// 写入字节
Files.write(path, bytes);
// 写入行
List<String> lines = Arrays.asList("line1", "line2");
Files.write(path, lines, StandardCharsets.UTF_8);
// 追加写入
Files.write(path, lines, StandardCharsets.UTF_8,
StandardOpenOption.APPEND);流式读写(大文件)
// 读取流
try (BufferedReader reader = Files.newBufferedReader(path,
StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
// 处理每行
}
}
// 写入流
try (BufferedWriter writer = Files.newBufferedWriter(path,
StandardCharsets.UTF_8)) {
writer.write("Hello");
writer.newLine();
writer.write("World");
}
// 输入流
try (InputStream is = Files.newInputStream(path)) {
// 读取
}
// 输出流
try (OutputStream os = Files.newOutputStream(path)) {
// 写入
}文件属性
Path path = Paths.get("test.txt");
// 基本属性
long size = Files.size(path);
FileTime lastModified = Files.getLastModifiedTime(path);
FileTime created = Files.getCreationTime(path);
boolean readable = Files.isReadable(path);
boolean writable = Files.isWritable(path);
boolean executable = Files.isExecutable(path);
// 获取所有属性
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
attrs.size();
attrs.lastModifiedTime();
attrs.isDirectory();
attrs.isRegularFile();
// 设置属性
Files.setAttribute(path, ":lastModifiedTime", FileTime.fromMillis(System.currentTimeMillis()));
Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis()));目录遍历
// 列出目录内容
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get("dir"))) {
for (Path entry : stream) {
System.out.println(entry.getFileName());
}
}
// 过滤文件
try (DirectoryStream<Path> stream = Files.newDirectoryStream(
Paths.get("dir"), "*.txt")) {
for (Path entry : stream) {
System.out.println(entry);
}
}
// 深度遍历(递归)
Files.walkFileTree(Paths.get("dir"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("文件: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
System.out.println("目录: " + dir);
return FileVisitResult.CONTINUE;
}
});
// 使用 Stream API
Files.walk(Paths.get("dir"))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Files.list(Paths.get("dir"))
.forEach(System.out::println);文件监控(WatchService)
Path dir = Paths.get("C:/temp");
WatchService watcher = FileSystems.getDefault().newWatchService();
dir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey key = watcher.take(); // 阻塞等待事件
for (WatchEvent<?> event : key.pollEvents()) {
Path changedFile = dir.resolve((Path) event.context());
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("创建: " + changedFile);
} else if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("删除: " + changedFile);
} else if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("修改: " + changedFile);
}
}
key.reset(); // 重置以继续监听
}实用示例
复制目录
public static void copyDirectory(Path source, Path target) throws IOException {
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
Path targetDir = target.resolve(source.relativize(dir));
Files.createDirectories(targetDir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.copy(file, target.resolve(source.relativize(file)),
StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}查找文件
// 查找所有 Java 文件
Files.walk(Paths.get("src"))
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
// 按条件查找
Path found = Files.walk(Paths.get("src"))
.filter(p -> p.getFileName().toString().equals("Main.java"))
.findFirst()
.orElse(null);面试要点总结
| 问题 | 答案要点 |
|---|---|
| Path vs File? | Path 更现代,支持符号链接、异常处理更好 |
| Files 常用方法? | copy/move/delete/readAllLines/write |
| 大文件怎么读? | newBufferedReader/newInputStream 流式读取 |
| 如何监控文件变化? | WatchService |
| 如何遍历目录? | walkFileTree/walk/list |
参考资料
- Java NIO.2 Tutorial (opens in a new tab)
- 《Java NIO》- Ron Hitchens