CodeGym /课程 /JAVA 25 SELF /检查文件和目录是否存在

检查文件和目录是否存在

JAVA 25 SELF
第 38 级 , 课程 1
可用

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("这是目录!");
}

建议:对于新项目,最好直接使用 NIOPathFiles),因为该 API 更现代、功能更全,并且与 try-with-resources 配合良好。

表格:方法对比

方式 存在性检查 类型检查(文件/文件夹) 现代程度
File.exists()
是(isFile(), isDirectory()) 较旧
Files.exists(Path)
是(isRegularFile(), isDirectory()) 推荐

2. TOCTOU 问题:为何“先检查”不是万能药

问题的本质是什么?

假设你已经检查过:“文件存在!”——随后立刻决定读取它。但在这两个动作之间,以处理器的尺度,可能过去了很长时间。期间文件可能被删除、移动、被其他进程替换,或者其访问权限被更改。

这就像你看了眼冰箱,确认里面有蛋糕,关上门,再次打开——却发现蛋糕已经没了。编程里也有这样的“室友”:其他进程、用户、杀毒软件,甚至文件系统本身。

为什么这很重要?

因此,即使你确认了文件存在,在尝试打开它时仍可能抛出异常——例如 FileNotFoundExceptionAccessDeniedException

所以检查并不是保证,只是额外的保险。务必时刻准备处理异常,并把它们捕获好!

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:使用 PathFiles

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'。");
        }
    }
}

即便检查过——仍要捕获异常!

在这两个示例中,尽管做了预先检查,我们依然使用了 trycatch 块,因为文件可能在任何时刻消失或变得不可访问。这是文件操作的黄金法则!

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:忽略访问权限。 文件或文件夹可能存在,但没有读/写权限。这类问题只会在尝试打开时暴露——务必使用 trycatch

错误 5:忽略不同操作系统上的文件名大小写差异。 在 Windows 上文件名大小写不敏感,而在 Linux 上则敏感。如果程序查找 "notes.txt",可能找不到 "Notes.txt"

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION