362. Java IO API - 操作文件和目录
操作文件和目录是编写文件 I/O 程序时最常见的任务之一。Java NIO 提供了功能强大的 Files 类和 Path 接口来简化这些操作。
✅ 1. 检查文件或目录是否存在
Path 实例本身只是一个路径字符串的封装,不代表真实存在的文件。要检查路径对应的文件是否真的存在,需要访问文件系统:
Files.exists(path) // 检查是否存在
Files.notExists(path) // 检查是否明确不存在
⚠️ 注意:
!Files.exists(path) 和 Files.notExists(path) 不等价!
三种可能的返回结果:
| exists(path) | notExists(path) | 含义 |
|---|---|---|
| true | false | 文件存在 |
| false | true | 文件不存在 |
| false | false | 无法判断(权限问题或IO错误) |
示例代码:
Path path = Paths.get("/path/to/file.txt");
if (Files.exists(path)) {
System.out.println("✅ 文件存在");
} else if (Files.notExists(path)) {
System.out.println("❌ 文件不存在");
} else {
System.out.println("⚠️ 无法判断文件状态(可能是权限问题)");
}
🔒 2. 检查文件的访问权限
你可以使用以下方法来检测文件是否可读、可写、可执行:
Files.isReadable(path)
Files.isWritable(path)
Files.isExecutable(path)
这些方法非常适合在文件处理前进行权限校验,例如:
示例:检查是否是一个可读并可执行的常规文件
Path file = Paths.get("run.sh");
boolean isExecutable = Files.isRegularFile(file)
&& Files.isReadable(file)
&& Files.isExecutable(file);
if (isExecutable) {
System.out.println("✅ 可执行的脚本文件");
} else {
System.out.println("❌ 文件不存在或不可执行");
}
🔐 注意:TOCTTOU 问题(Time-of-check to time-of-use) 在检查完权限之后立刻使用文件并不安全,因为文件状态在检查和使用之间可能发生变化。 建议直接尝试操作文件,并捕获异常来处理错误。
🔗 3. 判断两个路径是否引用同一个文件
在支持**符号链接(symbolic links)**的系统中,不同路径可能指向同一个文件。
此时可以使用:
Files.isSameFile(path1, path2)
该方法会解析符号链接并访问底层文件系统,判断两个 Path 是否指向同一个实体。
示例代码:
Path p1 = Paths.get("/home/user/docs/report.txt");
Path p2 = Paths.get("/home/user/shortcut/report.txt");
if (Files.isSameFile(p1, p2)) {
System.out.println("✅ 两个路径引用同一个文件");
} else {
System.out.println("❌ 路径对应的文件不同");
}
💡 若路径不存在,将抛出 IOException,建议使用 try-catch 块包裹调用。
📌 实战提示
| 操作需求 | 推荐方法 |
|---|---|
| 检查文件是否存在 | Files.exists(path) |
| 检查是否可读/写/执行 | Files.isReadable(path) 等 |
| 比较两个路径是否指向同一文件 | Files.isSameFile(path1, path2) |
| 文件是否是常规文件 | Files.isRegularFile(path) |
| 避免 TOCTTOU 安全问题 | 使用 try-catch 替代提前检查权限 |
✅ 总结
Path是语法结构,不代表真实文件。- 使用
Files.exists()和Files.notExists()与文件系统交互。 - 使用
Files.isReadable()等方法判断权限。 - 使用
Files.isSameFile()判断路径指向是否一致。 - 编写健壮程序应避免“检查后使用”的 TOCTTOU 漏洞,建议直接操作并处理异常。