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.
Таблиця: порівняння підходів
| Спосіб | Перевірка існування | Перевірка типу (файл/тека) | Сучасність |
|---|---|---|---|
|
Так | Так (isFile(), isDirectory()) | Застарілий |
|
Так | Так (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' не знайдено.");
}
}
}
Навіть після перевірки — перехоплюємо винятки!
В обох прикладах, попри попередню перевірку, ми все одно використовуємо блоки 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".
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ