CodeGym /Courses /JAVA 25 SELF /Checking for the existence of files and directories

Checking for the existence of files and directories

JAVA 25 SELF
Level 38 , Lesson 1
Available

1. Methods for checking the existence of files and directories

Working with files can feel like a walk through a minefield: you never know what awaits you beyond the next byte. The good news: Java gives us a “metal detector” — methods to check for the existence of files and folders.

The File class: checking via exists(), isFile(), isDirectory()

A classic way is to use the java.io.File class:

File file = new File("example.txt");
if (file.exists()) {
    System.out.println("File exists!");
} else {
    System.out.println("File not found.");
}

The exists() method returns true if a file or folder with that name exists. But that’s not all! Sometimes you need to know exactly what exists: a file or a directory.

if (file.isFile()) {
    System.out.println("This is a file.");
} else if (file.isDirectory()) {
    System.out.println("This is a directory.");
} else {
    System.out.println("Nothing found.");
}

The Path and Files classes: the modern approach

In modern code, it’s better to use the newer and more powerful java.nio.file API. Here, the static method Files.exists() is used to check for existence:

import java.nio.file.*;

Path path = Paths.get("example.txt");
if (Files.exists(path)) {
    System.out.println("File found via NIO!");
}

To check the object type, use:

if (Files.isRegularFile(path)) {
    System.out.println("This is a regular file!");
}
if (Files.isDirectory(path)) {
    System.out.println("This is a directory!");
}

Tip: for new projects, prefer NIO (Path, Files) right away because this API is more modern, supports more features, and plays nicely with try-with-resources.

Table: comparing the approaches

Approach Existence check Type check (file/folder) Modernity
File.exists()
Yes Yes (isFile(), isDirectory()) Legacy
Files.exists(Path)
Yes Yes (isRegularFile(), isDirectory()) Recommended

2. The TOCTOU problem: why checking is not a silver bullet

What’s the issue?

Suppose you checked: “File exists!” — and immediately decided to read it. But between these two actions an eternity can pass in CPU terms. During that time the file can be deleted, moved, replaced by another process, or its permissions can change.

It’s like peeking into the fridge, noting there’s cake, closing the door, and then opening it again — only to find the cake has been eaten. Programming has such “housemates,” too: other processes, users, antivirus tools, and the file system itself.

Why does this matter?

As a result, even if you checked that the file exists, trying to open it can still throw an exception — for example, FileNotFoundException or AccessDeniedException.

So a check is not a guarantee; it’s only an extra safety net. Always be prepared for exceptions and handle them!

3. Practice: check for a file before reading

Let’s add a function that prints a file’s contents if it exists, and informs the user if it does not. We’ll show two variants: via the old and the new API.

Option 1: via 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("Error reading file: " + e.getMessage());
            }
        } else {
            System.out.println("File 'notes.txt' not found.");
        }
    }
}

Option 2: via Path and 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("Error reading file: " + e.getMessage());
            }
        } else {
            System.out.println("File 'notes.txt' not found.");
        }
    }
}

Even after checking — catch exceptions!

In both examples, despite the preliminary check, we still use trycatch blocks, because a file can disappear or become inaccessible at any moment. This is the golden rule of working with files!

4. Checking for a directory’s existence

Similarly, you can check whether a folder exists and create it if it doesn’t:

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("Directory 'data' found.");
        } else {
            System.out.println("Directory 'data' not found. Creating...");
            try {
                Files.createDirectory(dir);
                System.out.println("Directory created!");
            } catch (IOException e) {
                System.out.println("Error creating directory: " + e.getMessage());
            }
        }
    }
}

By the way:
The Files.createDirectory() method throws an exception if the directory already exists. If you want to create a chain of directories (for example, "data/2025/09"), use Files.createDirectories(), which won’t complain if some of the directories already exist.

5. Details and gotchas: relative and absolute paths

Relative paths

When you write "notes.txt", the program looks for the file in the “current working directory.” Where that is depends on how and from where you run the application (IDE, terminal, double-clicking a JAR, etc.).

Absolute paths

If you need to be sure where to look, it’s better to use absolute paths or build them dynamically:

String userHome = System.getProperty("user.home");
Path filePath = Paths.get(userHome, "myapp", "notes.txt");

Checking the object’s type

Sometimes a “file” can unexpectedly turn out to be a directory. Therefore, check not only existence but also the type:

if (Files.isRegularFile(path)) {
    // This is a file!
}
if (Files.isDirectory(path)) {
    // This is a directory!
}

6. Demonstrating TOCTOU in practice

Let’s simulate a situation where the file disappears after the check but before opening. Run the code and delete the file manually between the check and the read:

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("File found, starting to read...");
            // At this point, open your file explorer and delete "notes.txt" manually!
            try {
                Files.lines(path).forEach(System.out::println);
            } catch (IOException e) {
                System.out.println("Oops! The file disappeared: " + e.getMessage());
            }
        } else {
            System.out.println("File not found.");
        }
    }
}

Result:
If you manage to delete the file between the check and the read, you’ll get an exception. This clearly shows that even a careful check doesn’t protect you from unexpected changes.

7. Common mistakes when checking for files and folders

Mistake #1: Relying only on a check and not using try-catch. “If the file exists, it’s safe to read it,” beginners think. But the file can disappear or become inaccessible, and your program will crash.

Mistake #2: Checking only existence, not the type. If you check only exists() without clarifying whether it’s a file or a directory, you can try to open a directory as a file — and get an error.

Mistake #3: Using relative paths without understanding the working directory. The program looks for the file “in the wrong place,” and the user can’t understand why nothing works.

Mistake #4: Ignoring permissions. A file or folder can exist, but you may lack read/write permissions. Such issues surface only when you try to open the file — always use trycatch.

Mistake #5: Ignoring case sensitivity across operating systems. On Windows, file names are case-insensitive, while on Linux they are case-sensitive. A program may not find "Notes.txt" if it’s looking for "notes.txt".

1
Task
JAVA 25 SELF, level 38, lesson 1
Locked
Exploration: searching for the "data" stash 🕵️‍♂️
Exploration: searching for the "data" stash 🕵️‍♂️
1
Task
JAVA 25 SELF, level 38, lesson 1
Locked
Creating a Safe for Valuables: Backup 🔐
Creating a Safe for Valuables: Backup 🔐
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION