JavaRush /Курсы /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: современный подход

В современных программах лучше использовать более новый и мощный API java.nio.file. Здесь для проверки существования используется статический метод 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.

Таблица: сравнение подходов

Способ Проверка существования Проверка типа (файл/папка) Современность
File.exists()
Да Да (isFile(), isDirectory()) Старый
Files.exists(Path)
Да Да (isRegularFile(), isDirectory()) Рекомендуется

2. Проблема TOCTOU: почему проверка — не панацея

В чём суть проблемы?

Допустим, вы проверили: «Файл существует!» — и тут же решили его прочитать. Но между этими двумя действиями может пройти вечность по меркам процессора. За это время файл может быть удалён, перемещён, заменён другим процессом, или его права доступа могут измениться.

Это как заглянуть в холодильник, отметить, что там есть торт, закрыть дверцу, а потом снова открыть её — и обнаружить, что торт уже съеден. В программировании тоже есть такие «домочадцы»: другие процессы, пользователи, антивирусы, сама файловая система.

Почему это важно?

В результате, даже если вы проверили, что файл существует, при попытке его открыть всё равно может возникнуть исключение — например, FileNotFoundException или AccessDeniedException.

Так что проверка — это не гарантия, а лишь дополнительная страховка. Всегда будьте готовы к исключениям и обрабатывайте их!

3. Практика: проверяем существование файла перед чтением

Добавим функцию, которая выводит содержимое файла, если он существует, и сообщает пользователю, если файла нет. Покажем два варианта: через старый и новый 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' не найден.");
        }
    }
}

Даже после проверки — ловим исключения!

В обоих примерах, несмотря на предварительную проверку, мы всё равно используем блоки 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".

1
Задача
JAVA 25 SELF, 38 уровень, 1 лекция
Недоступна
Разведка местности: ищем тайник "data" 🕵️‍♂️
Разведка местности: ищем тайник "data" 🕵️‍♂️
1
Задача
JAVA 25 SELF, 38 уровень, 1 лекция
Недоступна
Создание убежища для ценностей: резервная копия 🔐
Создание убежища для ценностей: резервная копия 🔐
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ