JavaRush /Курсы /JAVA 25 SELF /Исключения как часть API и try-with-resources

Исключения как часть API и try-with-resources

JAVA 25 SELF
24 уровень , 4 лекция
Открыта

1. Исключения как часть API

Почему исключения — это часть "контракта" метода?

Когда вы пишете метод, вы определяете не только параметры и возвращаемое значение, но и какие исключения он может выбросить. Это часть «контракта» между вашим методом и теми, кто его будет использовать. Если метод может выбросить исключение, об этом должны знать все, кто его вызывает — чтобы правильно обработать ошибку (или хотя бы не удивляться, когда программа внезапно завершится с ошибкой).

Пример:

public void readFile(String filename) throws IOException {
    // ... чтение файла
}

Здесь явно указано, что метод может выбросить IOException. Это сигнал для пользователя метода: «Будь готов обработать ошибку чтения файла!»

Документирование исключений: аннотация @throws в Javadoc

Чтобы другие разработчики понимали, какие исключения может выбросить ваш метод, используйте аннотацию @throws (или @exception) в Javadoc.

Пример:

/**
 * Читает содержимое файла.
 *
 * @param filename имя файла
 * @return содержимое файла в виде строки
 * @throws IOException если произошла ошибка чтения файла
 */
public String readFile(String filename) throws IOException {
    // ...
}

Зачем это нужно?

  • Помогает другим разработчикам понять, какие ошибки нужно обрабатывать.
  • IDE и генераторы документации (например, Javadoc) показывают эти исключения прямо в подсказках.
  • Повышает надёжность и предсказуемость кода.

Checked и unchecked исключения в API

Checked-исключения (наследники Exception, но не RuntimeException) — часть контракта метода. Их нужно либо обработать, либо явно пробросить дальше (throws).

Unchecked-исключения (RuntimeException и наследники) — обычно сигнализируют о программных ошибках (например, NullPointerException, IllegalArgumentException). Их не обязательно указывать в сигнатуре, но если ваш метод может выбросить такую ошибку (например, при некорректных аргументах), это тоже стоит описывать в Javadoc.

Пример:

/**
 * Делит a на b.
 * @param a делимое
 * @param b делитель
 * @return результат деления
 * @throws IllegalArgumentException если b == 0
 */
public int divide(int a, int b) {
    if (b == 0) throw new IllegalArgumentException("Делитель не может быть нулём");
    return a / b;
}

Исключения и проектирование API

  • Думайте о пользователе вашего метода: какие ошибки он может и должен обработать? Какие — это баги, а какие — «рабочие» ситуации?
  • Не злоупотребляйте checked-исключениями: если ошибка — это баг (например, неверный аргумент), лучше выбрасывать unchecked-исключение.
  • Документируйте все исключения, которые могут быть выброшены «наверх».

2. Конструкция try-with-resources

Проблема: как безопасно закрывать ресурсы?

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

Раньше:

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    // ...
} catch (IOException e) {
    // обработка ошибки
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            // обработка ошибки при закрытии
        }
    }
}

Много кода, легко ошибиться, можно забыть закрыть ресурс.

Решение: try-with-resources

С Java 7 появилась конструкция try-with-resources, которая автоматически закрывает все ресурсы, даже если внутри блока возникло исключение.

Синтаксис:

try (ResourceType resource = new ResourceType(...)) {
    // работа с ресурсом
} catch (ExceptionType e) {
    // обработка ошибки
}

Пример:

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Ошибка при чтении файла: " + e.getMessage());
}
// reader.close() вызовется автоматически!

Как это работает?

  • В скобках после try объявляются ресурсы, которые нужно закрыть.
  • После выхода из блока try (даже если возникло исключение!) для каждого ресурса вызывается метод close().
  • Это работает только для ресурсов, реализующих интерфейс AutoCloseable (или его предка Closeable).

Интерфейс AutoCloseable:

public interface AutoCloseable {
    void close() throws Exception;
}

Все стандартные ресурсы Java (файлы, потоки, соединения с БД) реализуют этот интерфейс.

Можно объявлять несколько ресурсов

try (
    BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
    BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))
) {
    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.newLine();
    }
}

Оба ресурса будут закрыты автоматически, даже если возникнет исключение.

Преимущества try-with-resources

  • Безопасность: ресурсы всегда закрываются, даже при ошибках.
  • Краткость: меньше кода, меньше шансов ошибиться.
  • Читаемость: сразу видно, какие ресурсы используются и когда они закрываются.

3. Практика: пишем безопасный код с try-with-resources

Пример: чтение файла

public static void printFirstLine(String filename) {
    try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
        String line = reader.readLine();
        System.out.println("Первая строка: " + line);
    } catch (IOException e) {
        System.out.println("Ошибка: " + e.getMessage());
    }
}

Пример: запись в файл

public static void writeToFile(String filename, String text) {
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
        writer.write(text);
    } catch (IOException e) {
        System.out.println("Ошибка при записи: " + e.getMessage());
    }
}

Пример: свой ресурс

Если вы пишете свой класс, который нужно закрывать, просто реализуйте AutoCloseable:

public class MyResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Ресурс закрыт!");
    }
}

Теперь его можно использовать в try-with-resources:

try (MyResource res = new MyResource()) {
    // работа с ресурсом
} 

4. Типичные ошибки и best practices

Ошибка №1: забыли закрыть ресурс (без try-with-resources).
Если не использовать try-with-resources, легко забыть закрыть файл или поток — это приводит к утечкам ресурсов.

Ошибка №2: попытка использовать try-with-resources с объектом, который не реализует AutoCloseable.
Если ваш класс не реализует этот интерфейс, компилятор не даст использовать его в try-with-resources.

Ошибка №3: не документируете исключения в API.
Если ваш метод может выбросить исключение — обязательно указывайте это в сигнатуре (throws) и в Javadoc (@throws). Это поможет другим правильно использовать ваш код.

Ошибка №4: ловите Exception вместо конкретных ошибок.
Лучше ловить только те исключения, которые реально ожидаете и умеете обрабатывать.

1
Задача
JAVA 25 SELF, 24 уровень, 4 лекция
Недоступна
Новостной агрегатор: Автоматическое чтение заголовка статьи
Новостной агрегатор: Автоматическое чтение заголовка статьи
1
Задача
JAVA 25 SELF, 24 уровень, 4 лекция
Недоступна
Магический артефакт: Самостоятельное отключение
Магический артефакт: Самостоятельное отключение
1
Задача
JAVA 25 SELF, 24 уровень, 4 лекция
Недоступна
Научная лаборатория: Документирование потенциальных сбоев в данных
Научная лаборатория: Документирование потенциальных сбоев в данных
1
Задача
JAVA 25 SELF, 24 уровень, 4 лекция
Недоступна
Цифровой архивариус: Безопасное копирование древних рукописей
Цифровой архивариус: Безопасное копирование древних рукописей
1
Опрос
Иерархия исключений, 24 уровень, 4 лекция
Недоступен
Иерархия исключений
Продвинутая работа с исключениями
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Alex Blizz Уровень 27
13 января 2026
При таком написании IDE не видит файл .

BufferedReader reader = new BufferedReader(new FileReader("ancient_manuscript.txt")
А если указать путь от корня, то IDE файл видит, но уже валидатор не принимает задачу.

src/ru/javarush/java/core/level24/task20/ancient_manuscript.txt
Можно ли как то настроить IDE чтоб она по умолчанию искала файл в директории текущего пакета?