1. 概述
本文将介绍在 Java 中判断文件或目录是否存在的几种常用方式。
我们会优先介绍现代的 NIO.2 API,然后再回顾一下传统的 java.io.File
方式。对于日常开发,强烈建议使用 NIO.2,更灵活、更高效,也能更好地处理符号链接等边缘情况。
2. 使用 java.nio.file.Files
✅ 推荐方式:使用 Files.exists(Path)
方法是目前最标准、最简洁的判断手段。
该方法接收一个 Path
对象,返回布尔值表示目标是否存在。使用前需通过 Paths.get()
获取 Path
实例。
Path path = Paths.get("does-not-exist.txt");
assertFalse(Files.exists(path));
⚠️ 注意:如果目标文件因权限问题无法访问,或发生
IOException
,此方法也会返回false
—— 这是“静默失败”机制,使用时需结合具体场景判断。
当文件真实存在时,返回 true
:
Path tempFile = Files.createTempFile("baeldung", "exist-article");
assertTrue(Files.exists(tempFile));
该方法对目录同样有效:
Path tempDirectory = Files.createTempDirectory("baeldung-exists");
assertTrue(Files.exists(tempDirectory));
2.1 判断具体类型:文件 or 目录?
如果需要明确区分是文件还是目录,可使用以下两个专用方法:
Files.isRegularFile(Path)
:判断是否为普通文件Files.isDirectory(Path)
:判断是否为目录
assertTrue(Files.isDirectory(tempDirectory));
assertFalse(Files.isDirectory(tempFile));
assertTrue(Files.isRegularFile(tempFile));
✅ 小贴士:
isRegularFile
不会将目录、符号链接等误判为文件,比exists()
+ 类型判断更精准。
2.2 判断“不存在”:notExists
Files.notExists(Path)
是 exists()
的反向判断。但注意,它并不是简单的 !exists()
,在某些文件系统边缘情况下行为更严谨。
assertFalse(Files.notExists(tempDirectory));
实际开发中,exists()
更常用,notExists()
多用于断言或特定条件判断。
2.3 权限检查:isReadable
有时候 exists()
返回 false
并非因为文件不存在,而是当前用户无权访问。此时可用 Files.isReadable(Path)
辅助判断:
assertTrue(Files.isReadable(tempFile));
assertFalse(Files.isReadable(Paths.get("/root/.bashrc")));
该方法仅在文件存在且可读时返回 true
,避免了“存在但不可读”导致的误判。
3. 使用 java.io.File
(传统方式)
⚠️ 不推荐用于新项目,但维护老代码时仍会遇到。
在 Java 7 之前,这是唯一选择。现在虽仍可用,但 API 设计相对陈旧,处理符号链接和异常的方式不如 NIO 灵活。
判断是否存在使用 exists()
方法:
assertFalse(new File("invalid").exists());
对文件和目录一视同仁,只要路径存在就返回 true
。
区分文件和目录使用:
isFile()
:是否为文件isDirectory()
:是否为目录
Path tempFilePath = Files.createTempFile("baeldung", "exist-io");
Path tempDirectoryPath = Files.createTempDirectory("baeldung-exists-io");
File tempFile = new File(tempFilePath.toString());
File tempDirectory = new File(tempDirectoryPath.toString());
assertTrue(tempFile.exists());
assertTrue(tempDirectory.exists());
assertTrue(tempFile.isFile());
assertFalse(tempDirectory.isFile());
assertTrue(tempDirectory.isDirectory());
assertFalse(tempFile.isDirectory());
权限判断使用 canRead()
:
assertTrue(tempFile.canRead());
assertFalse(new File("/root/.bashrc").canRead());
❌ 踩坑提醒:
canRead()
返回false
时,可能是文件不存在,也可能是无读权限,无法区分。NIO 的isReadable()
更明确。
4. 符号链接(Symbolic Links)处理
这是 NIO 相比传统 IO 的一大优势。
4.1 默认行为:跟随链接(Follow Links)
默认情况下,Files.exists()
会追踪符号链接指向的目标。**只有目标存在,才返回 true
**。
Path target = Files.createTempFile("baeldung", "target");
Path symbol = Paths.get("test-link-" + ThreadLocalRandom.current().nextInt());
Path symbolicLink = Files.createSymbolicLink(symbol, target);
assertTrue(Files.exists(symbolicLink)); // 目标存在,返回 true
删除目标后,链接“悬空”,exists()
返回 false
:
Files.deleteIfExists(target);
assertFalse(Files.exists(symbolicLink));
4.2 不跟随链接:LinkOption.NOFOLLOW_LINKS
若只想判断符号链接本身是否存在,可传入 LinkOption.NOFOLLOW_LINKS
:
assertTrue(Files.exists(symbolicLink, LinkOption.NOFOLLOW_LINKS));
此时即使目标已被删除,只要链接文件本身还在,就返回 true
。
4.3 判断是否为符号链接
使用 Files.isSymbolicLink(Path)
可直接判断:
assertTrue(Files.isSymbolicLink(symbolicLink));
assertFalse(Files.isSymbolicLink(target));
✅ 场景建议:处理配置文件、软链接部署目录时,务必考虑符号链接行为,避免误删或误判。
5. 总结
方法 | 推荐度 | 说明 |
---|---|---|
Files.exists(Path) |
✅✅✅ | 首选,简洁、语义清晰 |
Files.isRegularFile/isDirectory |
✅✅✅ | 类型判断更精准 |
Files.isReadable |
✅✅ | 权限+存在性双重校验 |
File.exists() |
⚠️ | 仅用于维护老代码 |
File.canRead() |
⚠️ | 无法区分“不存在”和“无权限” |
📌 最佳实践建议:
- 新项目一律使用
java.nio.file.Files
- 涉及符号链接时,明确是否需要
NOFOLLOW_LINKS
- 权限敏感操作,结合
isReadable
或isWritable
判断
所有示例代码已托管至 GitHub: https://github.com/baeldung/java-tutorials/tree/master/core-java-modules/core-java-io-3