JavaRush /Курси /JAVA 25 SELF /IOException і FileNotFoundException: обробка помилок

IOException і FileNotFoundException: обробка помилок

JAVA 25 SELF
Рівень 38 , Лекція 0
Відкрита

1. Ієрархія винятків введення‑виведення

У Java під час роботи з файлами й іншими зовнішніми ресурсами майже завжди доводиться мати справу з так званими винятками введення‑виведення. Це об’єкти, які викидаються (throw) у разі, якщо щось пішло не так під час читання або запису даних. Наприклад, якщо файл не знайдено, немає доступу або диск раптово «втомився».

Головні герої нашої лекції

У Java є ціла ієрархія таких винятків. Ось основні з них:

  • IOException — базовий клас для всіх помилок введення‑виведення. Якщо щось пішло не так із файлами, потоками чи мережею — майже завжди «винен» він (або хтось із його численних нащадків).
  • FileNotFoundException — «нащадок» IOException, який з’являється, якщо ви намагаєтеся відкрити файл, якого не існує, або шлях указано неправильно.

Інші похідні:

  • EOFException — теж прямий «нащадок» IOException. Сигналізує про те, що під час читання ми неочікувано дійшли до кінця файлу.
  • MalformedInputException — «онук»: наслідує CharacterCodingException, який, своєю чергою, наслідує IOException. Така помилка виникає, якщо файл не можна коректно інтерпретувати в указаному кодуванні (наприклад, очікували "UTF-8", а надійшла бита послідовність).
  • А ще є SocketException, ZipException та інші спеціалізовані «родичі», кожен зі своєю зоною відповідальності. Чим глибше в ієрархію, тим вужча й специфічніша ситуація.

Невелика спрощена схема:

java.lang.Exception
└── java.io.IOException
    ├── java.io.FileNotFoundException
    ├── java.io.EOFException
    ├── java.nio.charset.MalformedInputException
    └── ... (інші)

Цікавий факт:
У Java майже всі операції з файлами потребують оголошувати або обробляти IOException — це так звані checked exceptions. Компілятор не дасть вам забути про обробку помилок!

2. Коли і чому виникають ці винятки

Відкриття неіснуючого файлу

Найчастіший випадок — ви намагаєтеся відкрити файл, а його немає. Це як прийти на зупинку, а автобуса не існує навіть у розкладі.

FileInputStream fis = new FileInputStream("abracadabra.txt"); // Бац! FileNotFoundException

Спроба запису у файл без прав

Якщо ви намагаєтеся записати файл у папку, де у вас немає прав, отримаєте FileNotFoundException або IOException (залежно від ситуації).

FileOutputStream fos = new FileOutputStream("/system/secret.txt"); // Бац! FileNotFoundException або IOException

Помилки читання і запису через пошкодження носія

Іноді файл начебто є, але диск пошкоджено, файл зайнятий іншою програмою або раптово вимкнули світло — тоді ви отримаєте IOException із різними повідомленнями.

Інші причини

  • Файл відкрито лише для читання, а ви намагаєтеся записувати.
  • Шлях до файлу надто довгий або містить неприпустимі символи.
  • Файл використовується іншим процесом.
  • Диск переповнений.
  • Файл видалено іншим процесом між перевіркою та використанням.

3. Обробка за допомогою try-catch

Щоразу, коли ви працюєте з файлами, потоками, мережею — використовуйте try-catch. Це як подушка безпеки: якщо щось пішло не так, програма не впаде, а зможе коректно відреагувати.

Як правильно перехоплювати винятки?

У Java перехоплювати конкретніші винятки потрібно раніше, ніж загальні. Якщо поставити загальний catch (IOException e) першим, то вужчі, наприклад FileNotFoundException, просто не спрацюють — до них керування не дійде.

Правильна структура:

try {
    // Робота з файлом
} catch (FileNotFoundException e) {
    // Обробка ситуації "файл не знайдено"
} catch (IOException e) {
    // Обробка інших помилок введення-виведення
}

Чому так?
Тому що FileNotFoundException — це окремий випадок IOException. Якщо ви перехопите загальний випадок раніше, то окремий «не дійде» до свого catch.

Приклад коду: обробка помилок під час відкриття файлу

import java.io.*;

public class FileReaderExample {
    public static void main(String[] args) {
        String filename = "notes.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line = reader.readLine();
            System.out.println("Перший рядок файлу: " + line);
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("Файл не знайдено: " + filename);
        } catch (IOException e) {
            System.out.println("Помилка під час читання файлу: " + e.getMessage());
        }
    }
}

Зверніть увагу:
Навіть якщо ви перевірили, що файл існує, завжди використовуйте try-catch — файл може зникнути будь-якої миті (наприклад, його видалить інший процес).

4. Практика: пишемо код із обробкою помилок

Створимо прості приклади та зробимо так, щоб вони коректно реагували на відсутність файлу та інші помилки.

Крок 1: Спробуємо відкрити неіснуючий файл

import java.io.*;

public class NotesApp {
    public static void main(String[] args) {
        String filename = "my_notes.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line;
            System.out.println("Ваші нотатки:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("Ой! Файл із нотатками не знайдено: " + filename);
            System.out.println("Порада: створіть файл або перевірте назву.");
        } catch (IOException e) {
            System.out.println("Сталася помилка під час читання файлу: " + e.getMessage());
        }
    }
}

Крок 2: Додамо обробку для запису у файл

import java.io.*;

public class NotesWriter {
    public static void main(String[] args) {
        String filename = "my_notes.txt";
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true)); // append: true
            writer.write("Нова нотатка!\n");
            writer.close();
            System.out.println("Нотатку успішно додано!");
        } catch (IOException e) {
            System.out.println("Помилка під час запису у файл: " + e.getMessage());
        }
    }
}

Крок 3: Універсальний приклад із логуванням помилок

import java.io.*;

public class SafeFileCopier {
    public static void main(String[] args) {
        String source = "source.txt";
        String target = "target.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(source));
            BufferedWriter writer = new BufferedWriter(new FileWriter(target));
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine();
            }
            reader.close();
            writer.close();
            System.out.println("Файл скопійовано успішно!");
        } catch (FileNotFoundException e) {
            System.out.println("Файл не знайдено: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("Помилка введення-виведення: " + e.getMessage());
        }
    }
}

Порада:
У реальних застосунках варто логувати помилки не лише на екран, а й у файл або журнал, щоб згодом можна було розібратися, що саме пішло не так.

5. Таблиця: основні IO‑винятки

Виняток Коли виникає? Як обробити?
FileNotFoundException
Файл не знайдено, шлях не існує, немає прав Повідомити користувача, перевірити шлях/права, за потреби створити файл
EOFException
Неочікуваний кінець файлу під час читання Повідомити про пошкодження/неповноту, спробувати частково відновити дані
IOException
Загальна помилка введення‑виведення (диск, права, блокування) Перевірити деталі, коректно завершити операцію, за можливості повторити спробу
MalformedInputException
Некоректне кодування або структура файлу Повідомити про пошкодження, спробувати інше кодування/джерело

6. Типові помилки під час обробки IO‑винятків

Помилка № 1: Перехоплювати лише загальний Exception. Дуже спокусливо написати просто catch (Exception e), але тоді ви не зможете розрізнити, що саме пішло не так. Краще спочатку перехоплювати конкретні винятки (FileNotFoundException), а потім — загальний IOException.

Помилка № 2: Не закривати потоки у разі помилок. Якщо ви відкрили файл, а потім виник виняток, потік може залишитися незакритим. Використовуйте try-with-resources або закривайте ресурси у finally.

Помилка № 3: Ігнорувати повідомлення винятків. Не просто виводьте «Помилка!», а показуйте подробиці: e.getMessage(). Це допоможе швидше зрозуміти, що пішло не так.

Помилка № 4: Не обробляти FileNotFoundException під час запису. Багато хто думає, що FileNotFoundException — це лише про читання. Насправді він може виникати і під час запису (некоректний шлях, немає прав на створення файлу тощо).

Помилка № 5: Не перевіряти права доступу. Якщо програму запущено з обмеженими правами, багато операцій із файлами можуть завершитися помилкою. Завжди враховуйте це й інформуйте користувача.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ