JavaRush /Курсы /JAVA 25 SELF /finally и throw: завершение и генерация исключений

finally и throw: завершение и генерация исключений

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

1. Знакомство с блоком finally

Когда вы работаете с ресурсами — файлами, сетевыми соединениями, базами данных — важно быть уверенным, что они будут закрыты или освобождены всегда, даже если в процессе работы возникла ошибка. В Java для этого есть специальный блок — finally.

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

Блок finally — это часть конструкции try-catch-finally. Код внутри finally выполняется всегда (если он написан) — независимо от того, было ли исключение или нет. Даже если в try был return или выброшено исключение — finally всё равно выполнится (если только не выключили компьютер или не принудительно не завершили программу через System.exit(0)).

Синтаксис:

try {
    // Код, где может возникнуть исключение
} catch (ExceptionType e) {
    // Обработка ошибки
} finally {
    // Этот код выполнится всегда!
}

Пример

try {
    System.out.println("Начало работы");
    int result = 10 / 0; // тут произойдет ошибка
    System.out.println("Результат: " + result);
} catch (ArithmeticException e) {
    System.out.println("Ошибка: деление на ноль");
} finally {
    System.out.println("Этот код выполнится в любом случае");
}

Результат выполнения:

Начало работы
Ошибка: деление на ноль
Этот код выполнится в любом случае

Что происходит?

  1. В try мы пробуем разделить два числа и получаем ошибку.
  2. Если при делении возникла ошибка — catch её обработает.
  3. Но! В любом случае finally выполнится и напишет сообщение в консоль.

2. finally без catch

Может быть 3 варианта конструкции:

  • Полный: try-catch-finally
  • Без finally: try-catch
  • Без catch: try-finally

Третий вариант используется, когда перехватывать и обрабатывать ошибку будет метод на уровень выше. Но блок finally нужен, чтобы гарантировать выполнение определённого кода:

  • Закрытие файлов, сетевых соединений, баз данных.
  • Освобождение любых ресурсов (например, блокировок).
  • Логирование: запись информации о завершении операции.

Пример:

try {
    System.out.println("Делим числа");
    int result = 10 / 0;   // ошибка!
    System.out.println("Результат: " + result);
} finally {
    System.out.println("Блок finally выполнен");
}

Результат:

Делим числа
Блок finally выполнен
Exception in thread "main" java.lang.ArithmeticException: / by zero

Когда finally НЕ выполняется?

Почти всегда выполняется. Исключения — если:

  1. Принудительно завершили программу с помощью: System.exit(0).
  2. Принудительно «убили» поток, где выполняется finally.
  3. Компьютер выключили.

3. Оператор throw: как сгенерировать исключение самому

Иногда Java сама «бросает» исключения (например, деление на ноль, выход за границы массива). Но бывают ситуации, когда вы хотите явно сообщить: «Это ошибка! Я не могу продолжать выполнение!». Для этого в Java есть оператор throw.

Аналогия: Если вы в магазине и видите просроченный товар — вы «бросаете» жалобу. Так и в коде: если что-то не так — бросаете исключение.

Синтаксис throw

throw new ExceptionType("Сообщение об ошибке");

ExceptionType — любой класс, наследующийся от Throwable (обычно Exception или RuntimeException). В скобках — сообщение, которое поможет понять, что пошло не так.

Пример: проверка аргументов метода

public static int safeDivide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("Делитель не может быть равен нулю");
    }
    return a / b;
}

Использование:

public static void main(String[] args) {
    try {
        int result = safeDivide(10, 0);
        System.out.println("Результат: " + result);
    } catch (IllegalArgumentException e) {
        System.out.println("Ошибка: " + e.getMessage());
    }
}

Результат:

Ошибка: Делитель не может быть равен нулю

Когда использовать throw?

  • Проверка аргументов метода (например, если пришёл null или некорректные данные).
  • Проверка состояния объекта (например, попытка снять деньги со счёта, на котором 0 евро).
  • Внутри catch — если хотите «перебросить» исключение дальше (например, добавить дополнительную информацию).

4. Совмещение try-catch-finally и throw

Иногда эти конструкции работают вместе. Например, вы ловите одну ошибку, но после этого решаете бросить свою, более информативную.

public static int parseAndDivide(String text, int divisor) {
    try {
        int number = Integer.parseInt(text);
        if (divisor == 0) {
            throw new IllegalArgumentException("Делитель не может быть равен нулю");
        }
        return number / divisor;
    } catch (NumberFormatException e) {
        throw new IllegalArgumentException("Строка '" + text + "' не является числом");
    } finally {
        System.out.println("Попытка обработать строку: " + text);
    }
}

Использование:

try {
    int result = parseAndDivide("42a", 2);
    System.out.println("Результат: " + result);
} catch (IllegalArgumentException e) {
    System.out.println("Ошибка: " + e.getMessage());
}

Результат:

Попытка обработать строку: 42a
Ошибка: Строка '42a' не является числом

Важный нюанс: return и finally

Даже если в блоке try есть return, finally всё равно выполнится!

public static int getValue() {
    try {
        return 10;
    } finally {
        System.out.println("finally всё равно сработает!");
    }
}

Вызов getValue() выведет:

finally всё равно сработает!

5. Типичные ошибки при использовании finally и throw

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

Ошибка №2: выбросили исключение, но не обработали его.
Если вы бросаете исключение с помощью throw, но нигде его не ловите (нет try-catch), программа завершится аварийно. Всегда думайте, кто будет ловить ваше исключение.

Ошибка №3: return в finally.
Если вы по ошибке написали return внутри finally, он «перебьёт» все предыдущие return или throw. Это может привести к очень трудноуловимым багам. Так делать категорически не рекомендуется!

public int tricky() {
    try {
        return 1;
    } finally {
        return 2; // ОПАСНО: вернётся 2, а не 1!
    }
}

Результат: вернётся 2, хотя в try был 1.

Ошибка №4: потеря информации об исключении.
Если вы ловите одно исключение, а потом бросаете новое, не сохранив информацию о старом (e), вы теряете стек вызовов, что затрудняет отладку. Лучше писать так:

catch (NumberFormatException e) {
    throw new IllegalArgumentException("Ошибка преобразования", e);
}
1
Задача
JAVA 25 SELF, 11 уровень, 3 лекция
Недоступна
Робот-уборщик и его отчет о завершении 🧹
Робот-уборщик и его отчет о завершении 🧹
1
Задача
JAVA 25 SELF, 11 уровень, 3 лекция
Недоступна
Система подсчёта очков: только положительные значения! 🏅
Система подсчёта очков: только положительные значения! 🏅
1
Задача
JAVA 25 SELF, 11 уровень, 3 лекция
Недоступна
Критический модуль и аварийное завершение с отчетом 💥
Критический модуль и аварийное завершение с отчетом 💥
1
Задача
JAVA 25 SELF, 11 уровень, 3 лекция
Недоступна
Валидация имени пользователя в системе регистрации 📝
Валидация имени пользователя в системе регистрации 📝
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
hottabych007 Уровень 13
11 января 2026
Задачу: Валидация имени пользователя в системе регистрации 📝 - не смог решить сам😒. То ли лекция была не достаточно информативна, то ли устал сегодня - скорее всего последнее.
Grrbrr7 Уровень 21
5 ноября 2025
Сложна...