Питання 4 · Розділ 7

Які винятки потрібно обробляти обов'язково?

Компілятор Java змушує обробляти checked винятки. Це усі спадкоємці Exception, крім RuntimeException.

Мовні версії: English Russian Ukrainian

Junior Level

Checked винятки — обов’язкова обробка

Компілятор Java змушує обробляти checked винятки. Це усі спадкоємці Exception, крім RuntimeException.

Checked exceptions – це контракт між методом і його викликачем. Метод каже: «Я можу зіткнутися з X, і ти повинен бути до цього готовий.»

// Обов'язково: або try-catch, або throws
public void readFile() throws IOException {
    Files.readAllLines(Paths.get("file.txt"));
}

// Або обробка на місці
public void safeRead() {
    try {
        Files.readAllLines(Paths.get("file.txt"));
    } catch (IOException e) {
        log.error("Failed to read file", e);
    }
}

Поширені винятки для обробки

  • IOException — робота з файлами, мережею
  • SQLException — робота з базою даних
  • ClassNotFoundException — завантаження класів
  • InterruptedException — багатопоточність

Unchecked винятки — необов’язкова обробка

NullPointerException, IllegalArgumentException та інші RuntimeException можна не обробляти. Вони сигналізують про помилки в коді.

Базове правило

  • Checked — зовнішні збої (файли, мережа, БД) → обов’язково обробити
  • Unchecked — помилки програмування → повинні виправити код

Middle Level

Обов’язкова vs рекомендована обробка

Тип Обов’язок Хто вимагає
Checked Обов’язок обробити або оголосити throws Компілятор
Unchecked Рекомендується обробити Архітектура/команда
Error Не повинен перехоплювати JVM (відновлення неможливе)

Компіляторна перевірка

На рівні JVM розділення на checked та unchecked не існує. Інструкція athrow просто викидає об’єкт. Обов’язковість обробки — це лише перевірка javac.

Атрибут Exceptions у class-файлі

При оголошенні void method() throws IOException у .class файлі з’являється атрибут Exceptions. Компілятор використовує його для перевірки. Інші мови (Kotlin, Groovy) ігнорують цей атрибут.

Sneaky Throws — обхід перевірки

Можна кинути checked виняток без throws:

@SuppressWarnings("unchecked")
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
    throw (E) e; // Стирання типів обманює компілятор
}

// Lombok робить це автоматично
@SneakyThrows
public void readFile() {
    // IOException без throws!
    Files.readAllLines(Paths.get("file.txt"));
}

Exception Translation

Правильний підхід — перехоплювати checked винятки на межі шару і перекладати у доменні unchecked:

public interface Storage {
    byte[] read(String path); // Не залежить від SQL
}

public class SqlStorage implements Storage {
    public byte[] read(String path) {
        try {
            return jdbcTemplate.queryForObject(...);
        } catch (SQLException e) {
            throw new StorageException("Failed to read: " + path, e);
        }
    }
}

Senior Level

Функціональне програмування та checked exceptions

Стандартні інтерфейси (Function, Predicate, Supplier) не підтримують checked винятки:

// Не скомпілюється
stream.map(path -> Files.readString(path))

// Рішення 1 — обгортка
stream.map(path -> {
    try { return Files.readString(path); }
    catch (IOException e) { throw new UncheckedIOException(e); }
})

// Рішення 2 — Sneaky Throws
stream.map(path -> sneakyRead(path))

Продуктивність

Ніколи не використовуйте checked винятки для управління логікою. Кидання винятку — створення об’єкта + обхід стека — у 100-1000 разів повільніше повернення Optional.

Валідація вхідних даних

Для невалідних даних завжди використовуйте unchecked винятки (IllegalArgumentException). Це помилка програміста, а не зовнішнього світу.

InterruptedException – виняток, що підтверджує правило. Це не «control flow» у звичайному сенсі, а механізм кооперативної відміни потоків. Його обробка – частина контракту багатопоточності.

UncaughtExceptionHandler

Якщо checked виняток “просочився” через sneaky throws і не був спійманий, він дійде до обробника потоку. Встановіть глобальний обробник:

Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
    log.error("Uncaught exception in thread {}", thread.getName(), e);
});

Empty Catch Blocks — найгірший злочин

catch (IOException e) {} // Злочин!

Якщо повинні обробити, але не знаєте як — логуйте:

catch (IOException e) {
    log.error("IO error occurred", e);
    throw new RuntimeException(e);
}

Діагностика

  • Static Analysis — SonarQube блокує порожні catch-блоки
  • Micrometer — відстежуйте кількість винятків через метрики
  • Log Correlation — зберігайте Trace ID для всіх типів помилок

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Checked винятки — обов’язково обробити або оголосити throws (компілятор вимагає)
  • Unchecked — рекомендується обробити на рівні архітектури (не компілятор)
  • Error — НЕ повинен перехоплюватися (JVM у нестабільному стані)
  • На рівні JVM розділення немає — інструкція athrow викидає все однаково
  • Sneaky throws — обхід перевірки компілятора через стирання типів
  • Exception translation — перехоплення checked на межі шару → переклад у доменний unchecked
  • Для невалідних вхідних даних завжди використовувати unchecked (IllegalArgumentException)
  • Порожні catch-блоки — найгірший злочин: мінімум логувати

Часті уточнюючі запитання:

  • Як JVM обробляє checked vs unchecked? — Одинаково; розділення лише у javac
  • Що таке sneaky throws? — Обхід перевірки через @SneakyThrows (Lombok) або дженерик-стирання
  • Чому InterruptedException — особливий? — Це механізм кооперативної відміни потоків, потрібно відновити статус переривання
  • Що робити, якщо не знаєш як обробити? — Логувати і пробросити як RuntimeException

Червоні прапорці (НЕ говорити):

  • “Я залишаю порожній catch-блок, якщо не знаю що робити” — хоча б логуйте
  • “Я ловлю Error, щоб додаток не падав” — JVM нестабільна, це безглуздо
  • “Checked винятки — це те саме, що unchecked” — ні, компілятор перевіряє лише checked

Пов’язані теми:

  • [[Що таке checked виняток і коли його використовувати]]
  • [[Що таке unchecked виняток (Runtime Exception)]]
  • [[В чому різниця між Error та Exception]]
  • [[Чому не варто ковтати винятки (catch empty)]]
  • [[Що таке suppressed exceptions]]