Что такое Throwable?
Throwable -- класс, а не интерфейс, потому что хранит состояние: стек-трейс, сообщение, причину, suppressed-список. Интерфейс не может хранить данные.
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]]