1. 检查文件和目录是否存在的方法
与文件打交道有时像走雷区:谁也不知道下一个字节后面藏着什么。好消息是:Java 给了我们“金属探测器”——用于检查文件和文件夹是否存在的方法。
File 类:通过 exists()、isFile()、isDirectory() 检查
经典做法——使用 java.io.File 类:
File file = new File("example.txt");
if (file.exists()) {
System.out.println("文件存在!");
} else {
System.out.println("未找到文件。");
}
方法 exists() 在具有同名文件或文件夹时返回 true。但这还不够!有时我们需要知道具体是什么:文件还是目录。
if (file.isFile()) {
System.out.println("这是文件。");
} else if (file.isDirectory()) {
System.out.println("这是文件夹。");
} else {
System.out.println("什么也没找到。");
}
Path 和 Files 类:现代做法
在现代程序中,更好的方式是使用更新且更强大的 java.nio.file API。这里用于检查存在性的静态方法是 Files.exists():
import java.nio.file.*;
Path path = Paths.get("example.txt");
if (Files.exists(path)) {
System.out.println("通过 NIO 找到文件!");
}
要检查对象类型,请使用:
if (Files.isRegularFile(path)) {
System.out.println("这是常规文件!");
}
if (Files.isDirectory(path)) {
System.out.println("这是目录!");
}
建议:对于新项目,最好直接使用 NIO(Path、Files),因为该 API 更现代、功能更全,并且与 try-with-resources 配合良好。
表格:方法对比
| 方式 | 存在性检查 | 类型检查(文件/文件夹) | 现代程度 |
|---|---|---|---|
|
是 | 是(isFile(), isDirectory()) | 较旧 |
|
是 | 是(isRegularFile(), isDirectory()) | 推荐 |
2. TOCTOU 问题:为何“先检查”不是万能药
问题的本质是什么?
假设你已经检查过:“文件存在!”——随后立刻决定读取它。但在这两个动作之间,以处理器的尺度,可能过去了很长时间。期间文件可能被删除、移动、被其他进程替换,或者其访问权限被更改。
这就像你看了眼冰箱,确认里面有蛋糕,关上门,再次打开——却发现蛋糕已经没了。编程里也有这样的“室友”:其他进程、用户、杀毒软件,甚至文件系统本身。
为什么这很重要?
因此,即使你确认了文件存在,在尝试打开它时仍可能抛出异常——例如 FileNotFoundException 或 AccessDeniedException。
所以检查并不是保证,只是额外的保险。务必时刻准备处理异常,并把它们捕获好!
3. 实践:读取前先检查文件是否存在
我们添加一个函数:如果文件存在就输出其内容;如果不存在就提示用户。展示两种方式:老 API 和新 API。
方案 1:使用 File
import java.io.*;
public class FileExistenceCheck {
public static void main(String[] args) {
File file = new File("notes.txt");
if (file.exists() && file.isFile()) {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("读取文件时出错:" + e.getMessage());
}
} else {
System.out.println("未找到文件 'notes.txt'。");
}
}
}
方案 2:使用 Path 和 Files
import java.nio.file.*;
import java.io.IOException;
public class PathExistenceCheck {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
if (Files.exists(path) && Files.isRegularFile(path)) {
try {
Files.lines(path).forEach(System.out::println);
} catch (IOException e) {
System.out.println("读取文件时出错:" + e.getMessage());
}
} else {
System.out.println("未找到文件 'notes.txt'。");
}
}
}
即便检查过——仍要捕获异常!
在这两个示例中,尽管做了预先检查,我们依然使用了 try–catch 块,因为文件可能在任何时刻消失或变得不可访问。这是文件操作的黄金法则!
4. 检查目录是否存在
同理也可以检查文件夹是否存在;如果不存在——就创建它:
import java.nio.file.*;
public class DirectoryCheck {
public static void main(String[] args) {
Path dir = Paths.get("data");
if (Files.exists(dir) && Files.isDirectory(dir)) {
System.out.println("找到文件夹 'data'。");
} else {
System.out.println("未找到文件夹 'data'。正在创建...");
try {
Files.createDirectory(dir);
System.out.println("文件夹已创建!");
} catch (IOException e) {
System.out.println("创建文件夹时出错:" + e.getMessage());
}
}
}
}
顺便说一下:
方法 Files.createDirectory() 如果文件夹已存在会抛出异常。如果你想创建一串嵌套目录(例如 "data/2025/09"),请使用 Files.createDirectories(),当部分目录已存在时它不会报错。
5. 要点与细节:相对路径与绝对路径
相对路径
当你写 "notes.txt" 时,程序会在“当前工作目录”中查找文件。它在哪,取决于你如何以及从哪里运行应用(IDE、终端、双击 JAR 等)。
绝对路径
如果需要明确查找位置,最好使用绝对路径,或动态构建它们:
String userHome = System.getProperty("user.home");
Path filePath = Paths.get(userHome, "myapp", "notes.txt");
检查对象类型
有时“文件”可能出乎意料地是一个目录。因此不仅要检查是否存在,也要检查类型:
if (Files.isRegularFile(path)) {
// 这确实是一个文件!
}
if (Files.isDirectory(path)) {
// 这是一个文件夹!
}
6. 实操演示 TOCTOU 问题
我们来模拟一种情况:文件在检查之后、打开之前消失。运行代码,并在检查与读取之间手动删除文件:
import java.io.*;
import java.nio.file.*;
public class TOCTOUExample {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
if (Files.exists(path)) {
System.out.println("找到文件,马上开始读取...");
// 此时请打开资源管理器并手动删除 "notes.txt"!
try {
Files.lines(path).forEach(System.out::println);
} catch (IOException e) {
System.out.println("哎呀!文件消失了:" + e.getMessage());
}
} else {
System.out.println("未找到文件。");
}
}
}
结果:
如果你能在检查与读取之间删除文件,就会得到一个异常。这直观地说明,即便再谨慎的检查也无法避免不可预期的变化。
7. 检查文件和文件夹是否存在时的常见错误
错误 1:只依赖检查而不使用 try-catch。 许多新手会想:“既然文件在,那就可以放心读取。”但文件可能消失或变得不可访问,程序就会“崩溃”。
错误 2:只检查存在性,不检查类型。 如果你只检查 exists() 而不确认是文件还是文件夹,可能会把文件夹当作文件来打开——从而出错。
错误 3:不了解工作目录就使用相对路径。 程序会在“错误的位置”查找文件,用户也就不明白为何程序不工作。
错误 4:忽略访问权限。 文件或文件夹可能存在,但没有读/写权限。这类问题只会在尝试打开时暴露——务必使用 try–catch。
错误 5:忽略不同操作系统上的文件名大小写差异。 在 Windows 上文件名大小写不敏感,而在 Linux 上则敏感。如果程序查找 "notes.txt",可能找不到 "Notes.txt"。
GO TO FULL VERSION