Files与Paths
Java 7 引入了 NIO.2 文件 API,其中最常用的是 Path、Paths 和 Files。它们比传统 File 更适合现代 Java 项目。
# 1. Path
Path 表示文件系统路径。
Path path = Path.of("data", "app.log");
在 Java 11 之前常见写法是:
Path path = Paths.get("data", "app.log");
Path.of 和 Paths.get 都可以创建路径对象。新代码可以优先使用 Path.of。
# 2. Files
Files 是文件操作工具类,提供大量静态方法。
判断:
Files.exists(path);
Files.isRegularFile(path);
Files.isDirectory(path);
Files.isReadable(path);
创建:
Files.createFile(path);
Files.createDirectory(path);
Files.createDirectories(path);
复制、移动、删除:
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.delete(path);
Files.deleteIfExists(path);
# 3. 读写小文件
小文本文件可以直接读写:
String content = Files.readString(path, StandardCharsets.UTF_8);
Files.writeString(path, content, StandardCharsets.UTF_8);
读取所有行:
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
这些方法简单,但会把内容一次性读入内存,不适合大文件。
# 4. 读写大文件
大文件应使用流式处理:
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
// process line
}
}
写入:
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
writer.write("hello");
writer.newLine();
}
# 5. OpenOption
写文件时可以指定打开选项:
Files.writeString(
path,
"new line\n",
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
常见选项:
CREATE:不存在则创建。CREATE_NEW:必须创建新文件,已存在则失败。APPEND:追加写入。TRUNCATE_EXISTING:清空已有内容。WRITE:写入。READ:读取。
# 6. 文件属性
long size = Files.size(path);
FileTime modifiedTime = Files.getLastModifiedTime(path);
boolean hidden = Files.isHidden(path);
读取基础属性:
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println(attrs.creationTime());
System.out.println(attrs.lastModifiedTime());
System.out.println(attrs.size());
# 7. Files 比 File 好在哪里
| 对比项 | File | Files/Path |
|---|---|---|
| 错误信息 | 很多方法返回 boolean | 抛出明确异常 |
| 符号链接 | 支持有限 | 支持更好 |
| 文件属性 | 较弱 | 更丰富 |
| 路径处理 | 较弱 | 更现代 |
| 大文件遍历 | 不够方便 | walk、find、walkFileTree |
# 专家实践清单
| 场景 | 推荐 API | 注意点 |
|---|---|---|
| 拼接路径 | path.resolve | 避免字符串拼接路径 |
| 创建目录 | Files.createDirectories | 幂等创建多级目录 |
| 读取小文本 | Files.readString | 只适合小文件 |
| 流式读行 | Files.lines | 返回 Stream 要关闭 |
| 复制/移动 | Files.copy/move | 明确覆盖和原子移动策略 |
现代文件 API 分层:
Path
│ 表示路径
▼
Files
│ 执行文件操作
▼
FileSystem
│ 适配不同文件系统
# Tips 快问快答
Q:Path 和 Files 分别负责什么?
A:Path 表示路径,Files 提供文件操作方法。
Q:为什么 Files 比 File 更适合新代码?
A:失败时抛出明确异常,能力覆盖文件属性、链接、遍历等现代场景。
Q:Path.of 和 Paths.get 怎么选?
A:现代代码可优先 Path.of,语义更直接。
Q:Files.exists 之后再读取一定安全吗?
A:不一定。检查和使用之间文件可能变化,仍要处理异常。
Q:Files.lines 有什么坑?
A:它持有底层文件资源,必须关闭 Stream。
Q:Files.readAllLines 适合大文件吗?
A:不适合,会把所有行读入内存。
Q:移动文件时为什么关注 ATOMIC_MOVE?
A:它能避免目标文件出现半更新状态,但依赖文件系统支持。
Q:路径安全最重要的检查是什么? A:用户输入路径要 normalize 后确认仍在允许的根目录内。
# 8. 总结
新项目中应优先使用 Path 和 Files。File 仍然会在老代码和第三方 API 中出现,但现代文件操作的主力已经是 NIO.2 文件 API。