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

Что такое Throwable?

Throwable -- класс, а не интерфейс, потому что хранит состояние: стек-трейс, сообщение, причину, suppressed-список. Интерфейс не может хранить данные.

Версии по языкам: English Russian Ukrainian

Junior Level

Определение

java.lang.Throwable — это базовый класс для всех объектов, которые можно выбросить (throw) и перехватить (catch). Без наследования от Throwable объект нельзя использовать как исключение.

Throwable – класс, а не интерфейс, потому что хранит состояние: стек-трейс, сообщение, причину, suppressed-список. Интерфейс не может хранить данные.

Иерархия

Throwable
  ├── Error — фатальные ошибки JVM
  │     ├── OutOfMemoryError
  │     └── StackOverflowError
  └── Exception — ошибки приложения
        ├── RuntimeException (unchecked)
        └── Checked Exceptions

Основные методы

Throwable t = new Exception("Something went wrong");

t.getMessage();       // "Something went wrong"
t.printStackTrace();  // Печатает стек вызовов в System.err
t.getCause();         // Возвращает причину (если есть)
t.getStackTrace();    // Массив StackTraceElement[]

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

Напрямую Throwable используется редко. Обычно работают с его наследниками:

  • Exception и подклассы — для ошибок приложения
  • Error и подклассы — для фатальных ошибок JVM (но их не ловят)

Middle Level

Когда НЕ использовать catch(Throwable)

Никогда не используйте catch (Throwable t) в бизнес-логике – это перехватит OutOfMemoryError и оставит JVM в нестабильном состоянии. Исключение: глобальный обработчик на границе приложения.

Внутреннее устройство

Throwable содержит скрытое поле backtrace (тип Object), которое заполняется нативным методом fillInStackTrace(). Это поле хранит нативный стек вызовов в памяти JVM.

При вызове getStackTrace() нативная структура преобразуется в массив StackTraceElement[] — это ленивая оптимизация.

Конструктор с контролем стек-трейса

public Throwable(String message, 
                 Throwable cause, 
                 boolean enableSuppression, 
                 boolean writableStackTrace)

Параметры:

  • enableSuppression – разрешать ли suppressed exceptions // Suppressed exceptions – см. файл ‘Что такое suppressed exceptions.md’. // Кратко: если в try упало одно исключение, а в close() – другое, // второе не теряется, а добавляется в suppressed-список первого.
  • writableStackTrace — заполнять ли стек-трейс

Suppressed Exceptions

Механизм для сохранения “потерянных” исключений:

try (FileInputStream fis = new FileInputStream("file.txt")) {
    fis.read(); // IOException #1
} // close() бросает IOException #2
// #1 — основное, #2 — suppressed

При логировании через Logback/Log4j2 suppressed исключения выводятся с префиксом Suppressed:.

Сериализация

При передаче по сети (RMI, микросервисы) нативный backtrace теряется. JVM конвертирует его в Java-объекты, что увеличивает размер данных.


Senior Level

Отключение стек-трейса для производительности

Для исключений-сигналов (валидация) стек-трейс не нужен:

public class FastException extends RuntimeException {
    public FastException(String message) {
        super(message, null, true, false); // writableStackTrace = false
    }
}

Ускоряет создание в десятки раз — нативный обход стека не выполняется.

Immutable Exceptions

В высоконагруженных системах создают статические экземпляры:

public static final ValidationException INVALID_EMAIL = 
    new ValidationException("Invalid email", null, true, false);

Преимущества: не создаёт объекты на каждый вызов, нет нагрузки на GC. Недостатки: одинаковый стек-трейс у всех случаев.

Распределённые системы

Передача полных стек-трейсов — антипаттерн:

// На границе сервиса
try {
    service.process();
} catch (Throwable t) {
    // Передаём только DTO, а не весь стек
    return ErrorResponse.of(t.getClass().getSimpleName(), t.getMessage());
}

Экономит мегабайты трафика при массовых сбоях.

printStackTrace() — никогда в продакшне

printStackTrace() пишет в System.err, который:

  • Синхронизирован — блокировки потоков
  • Не попадает в лог-файлы без редиректа
  • В Kubernetes разбивается на отдельные строки

Используйте логгеры: log.error("Msg", throwable).

Диагностика

  • toString() — только имя класса и сообщение
  • printStackTrace() — всё дерево (медленно)
  • getStackTrace() — массив элементов (для программного анализа)
  • В распределённых системах используйте Trace ID / Span ID для связи логов между сервисами

🎯 Шпаргалка для интервью

Обязательно знать:

  • Throwable — базовый класс для всех объектов, которые можно throw и catch
  • Наследники: Error (фатальные JVM) и Exception (ошибки приложения)
  • Хранит состояние: стек-трейс, сообщение, причину (cause), suppressed-список
  • Ключевые методы: getMessage(), getCause(), getStackTrace(), printStackTrace()
  • Содержит скрытое поле backtrace, заполняемое нативным fillInStackTrace()
  • Конструктор с writableStackTrace = false отключает обход стека (ускоряет в 10-50 раз)
  • Suppressed exceptions — сохраняют исключения из close() при try-with-resources
  • printStackTrace() — никогда в продакшне (пишет в System.err, синхронизирован)

Частые уточняющие вопросы:

  • Почему Throwable — класс, а не интерфейс? — Хранит состояние (стек-трейс, cause, suppressed), интерфейсы не хранят данные
  • Когда НЕ использовать catch(Throwable)? — В бизнес-логике: перехватит OutOfMemoryError; допустимо только в глобальном обработчике
  • Что такое suppressed exceptions? — Если в try и close() упали два исключения, второе добавляется в suppressed первого
  • Почему printStackTrace() плох для продакшна? — Синхронизирован, не попадает в лог-файлы, в Kubernetes разбивается на строки

Красные флаги (НЕ говорить):

  • “Я использую Throwable вместо Exception для всех ошибок” — это перехватит и Error тоже
  • “printStackTrace() — нормальный способ логирования” — используйте логгеры
  • “Throwable не сериализуется” — сериализуется, но нативный backtrace теряется

Связанные темы:

  • [[Что находится в вершине иерархии исключений]]
  • [[В чём разница между Error и Exception]]
  • [[Что такое suppressed exceptions]]
  • [[Как правильно логировать исключения]]
  • [[Что такое exception chaining]]