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: Не проверять права доступа. Если программа запускается с ограниченными правами, многие операции с файлами могут завершиться ошибкой. Всегда учитывайте это и информируйте пользователя.

1
Задача
JAVA 25 SELF, 38 уровень, 0 лекция
Недоступна
Поиск потерянной записи в древней библиотеке 📚
Поиск потерянной записи в древней библиотеке 📚
1
Задача
JAVA 25 SELF, 38 уровень, 0 лекция
Недоступна
Запись важного донесения в королевский архив 📜
Запись важного донесения в королевский архив 📜
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Andrey Уровень 1
1 октября 2025
38