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

Що таке unchecked виняток (Runtime Exception)?

Базові unchecked винятки (NPE, IllegalArgumentException) сигналізують про баги. Проте доменні unchecked винятки (UserNotFoundException, OrderAlreadyExistsException) -- це очікув...

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

Junior Level

Визначення

Unchecked exceptions (неперевірювані винятки) — це винятки, які успадковуються від RuntimeException. Компілятор не вимагає їх обробляти.

Ієрархія

Throwable
  └── Exception
        └── RuntimeException  ← unchecked
              ├── NullPointerException
              ├── IllegalArgumentException
              ├── IndexOutOfBoundsException
              ├── ArithmeticException
              └── ClassCastException

Приклади

// NullPointerException — найчастіший
String s = null;
s.length(); // NPE

// IllegalArgumentException
public void setAge(int age) {
    if (age < 0) throw new IllegalArgumentException("Age must be positive");
}

// ArithmeticException
int x = 10 / 0; // ArithmeticException

Коли виникають

  • Помилки програмування (null, невірні аргументи)
  • Порушення контрактів методів
  • Логічні помилки в коді

Чи потрібно обробляти?

Базові unchecked винятки (NPE, IllegalArgumentException) сигналізують про баги. Проте доменні unchecked винятки (UserNotFoundException, OrderAlreadyExistsException) – це очікувані бізнес-ситуації, а не баги.


Middle Level

Коли НЕ використовувати unchecked винятки

  1. Публічні бібліотеки для сторонніх розробників – checked винятки накладають корисну обробку
  2. Команда без культури обробки помилок – checked = страховка від забуття
  3. Критичні системи – якщо ігнорування помилки = фінансові втрати, checked змусять обробити

Under the Hood: fillInStackTrace()

Головна “ціна” винятку — нативний метод fillInStackTrace(), який обходить стек потоку. У високонавантажених системах (100k+ RPS) генерація винятків може забирати 20-30% CPU.

Fail-Fast принцип

Fail-Fast – принцип: помилка повинна проявитися якомога раніше, а не «протікати» через 10 шарів коду. NPE на рядку s.length() миттєво вказує на проблему, а не «спливе» через годину роботи.

Unchecked винятки — ідеальний інструмент для реалізації Fail-Fast:

public User findUser(Long id) {
    Objects.requireNonNull(id, "ID cannot be null");
    // Якщо null — впадемо одразу, а не через 10 шарів
    return repository.findById(id)
        .orElseThrow(() -> new UserNotFoundException("User not found: " + id));
}

Централізована обробка

У Spring Boot використовується @ControllerAdvice + @ExceptionHandler:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(UserNotFoundException e) {
        return ResponseEntity.status(404)
            .body(new ErrorResponse("USER_NOT_FOUND", e.getMessage()));
    }

    @ExceptionHandler(ValidationException.class)
    public ResponseEntity<ErrorResponse> handleValidation(ValidationException e) {
        return ResponseEntity.status(400)
            .body(new ErrorResponse("VALIDATION_ERROR", e.getMessage()));
    }
}
// Потік: Виняток «спливає» через усі шари сервісу →
// Spring знаходить @ExceptionHandler з підходящим типом →
// Викликає метод → ResponseEntity перетворюється на JSON-відповідь.

Чому Spring пішов в unchecked

  1. Чистота коду — без try-catch шуму
  2. Централізована обробка — усі помилки спливають до @ControllerAdvice
  3. Reactive programming — у Project Reactor checked винятки практично неможливі

Senior Level

Продуктивність та оптимізація

Pre-instantiated Exceptions — для часто виникаючих помилок можна створити синглтон-виняток без стек-трейсу:

public static final ValidationException INVALID_FORMAT =
    new ValidationException("Invalid format", null, false, false);

Цей виняток не створює навантаження на GC, але ускладнює дебаг (немає стек-трейсу).

Control Flow антипатерн

Ніколи не використовуйте винятки для управління логікою:

// ПОГАНО — повільно і нечитабельно
try {
    User user = findUser(id);
} catch (UserNotFoundException e) {
    // перехід до наступного кроку
}

// ДОБРЕ
Optional<User> user = findUser(id);
if (user.isPresent()) { ... }

Повернення Optional у 100-1000 разів швидше, ніж кидання винятку.

Error vs RuntimeException

Не плутайте їх:

  • Error — проблеми JVM (OutOfMemoryError, StackOverflowError). Ловити — погана практика
  • RuntimeException — помилки додатку. Обробляти централізовано

Swallowing Exceptions

Найгірший злочин:

catch (RuntimeException e) { } // Мовчки ковтає баг

Діагностика та моніторинг

  • Micrometer — рахуйте кількість RuntimeException. Різкий ріст NPE — привід для відкату деплою
  • Log Enrichment — логуйте контекст (userId, orderId), а не лише стек-трейс
  • Metric-driven Exception Tracking — алертинг на аномалії у винятках

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

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

  • Unchecked exceptions успадковуються від RuntimeException, компілятор не вимагає обробки
  • Базові unchecked (NPE, IllegalArgumentException) сигналізують про баги; доменні (UserNotFoundException) — про бізнес-ситуації
  • Fail-Fast — помилка повинна проявитися якомога раніше
  • Spring Boot обробляє unchecked централізовано через @ControllerAdvice + @ExceptionHandler
  • fillInStackTrace() — найдорожча частина, може забирати 20-30% CPU при 100k+ RPS
  • Преінстанційовані винятки (синглтони) знижують навантаження на GC
  • Ніколи не використовувати винятки для control flow — Optional у 100-1000 разів швидше
  • Swallowing exceptions (catch (RuntimeException e) {}) — найгірший злочин

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

  • Чому Spring надає перевагу unchecked exceptions? — Чистота коду, централізована обробка через @ControllerAdvice, сумісність з Reactive programming
  • Коли unchecked винятки НЕ рекомендуються? — Публічні бібліотеки, команда без культури обробки помилок, критичні фінансові системи
  • Як прискорити створення частих винятків? — Pre-instantiated exceptions з writableStackTrace = false
  • Чим Error відрізняється від RuntimeException? — Error — проблеми JVM (не ловити), RuntimeException — помилки додатку (обробляти централізовано)

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

  • “Я використовую винятки для управління бізнес-логікою” — це антипатерн, повільно і нечитабельно
  • “Unchecked винятки не потрібно обробляти взагалі” — потрібно, але централізовано
  • “Мовчазне проковтування RuntimeException — нормальна практика” — це приховує баги

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

  • [[В чому різниця між checked та unchecked винятками]]
  • [[Що таке checked виняток і коли його використовувати]]
  • [[В чому різниця між Error та Exception]]
  • [[Що таке stack trace]]
  • [[Як правильно логувати винятки]]