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));

若只想判断符号链接本身是否存在,可传入 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
  • 权限敏感操作,结合 isReadableisWritable 判断

所有示例代码已托管至 GitHub: https://github.com/baeldung/java-tutorials/tree/master/core-java-modules/core-java-io-3


原始标题:Check If a File or Directory Exists in Java