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

Є три варіанти конструкції:

  • Повний: 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);
}
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ