Вопрос 3 · Раздел 7

Что такое unchecked exception (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 exceptions]]
  • [[Что такое checked exception и когда его использовать]]
  • [[В чём разница между Error и Exception]]
  • [[Что такое stack trace]]
  • [[Как правильно логировать исключения]]