В чём разница между Error и Exception?
Оба класса наследуются от Throwable, но означают принципиально разные проблемы:
Junior Level
Основное различие
Оба класса наследуются от Throwable, но означают принципиально разные проблемы:
Error – проблемы JVM (не приложения), которые обычно невозможно исправить.
Исключение: AssertionError технически Error, но возникает из-за логики приложения. Используется для проверки инвариантов, а не внешних сбоев.
- Не под контролем разработчика
- В обычном коде не ловите
- Примеры:
OutOfMemoryError,StackOverflowError
Exception — ошибки приложения:
- Ожидаемые сбои, которые можно обработать
- Нужно обрабатывать
- Примеры:
IOException,SQLException
В обычном коде не ловите Error. Исключения: глобальный обработчик потоков (setDefaultUncaughtExceptionHandler), graceful shutdown, health-check механизмы.
Визуально
Throwable
├── Error ← JVM сломалась
└── Exception ← Приложение столкнулось с проблемой
Примеры
// Error — приложение бессильно
// java.lang.OutOfMemoryError: Java heap space
List<byte[]> memoryHog = new ArrayList<>();
while (true) memoryHog.add(new byte[1024 * 1024]);
// Exception — можно обработать
try {
Files.readAllLines(Paths.get("missing.txt"));
} catch (IOException e) {
System.out.println("File not found, using default");
}
Простое правило
- Error — “умерла JVM” → пусть упадёт
- Exception — “что-то пошло не так” → обработать или залогировать
Middle Level
OutOfMemoryError — подтипы
OutOfMemoryError бывает разных видов:
Java heap space— закончилась кучаMetaspace— закончилась память под метаданные (часто при генерации классов через CGLIB)Unable to create new native thread— ОС запретила создавать новые потоки
Даже если поймать OOM, JVM может быть в нестабильном состоянии.
NoClassDefFoundError vs ClassNotFoundException
Это классический вопрос на собеседовании:
ClassNotFoundException (Exception):
- Вы явно просите загрузить класс:
Class.forName("com.mysql.Driver") - Класса нет в classpath
- Можно обработать
NoClassDefFoundError (Error):
- Класс был при компиляции, но пропал в рантайме
- Или его загрузка упала из-за ошибки в статическом блоке
- Критический сбой среды
// Если в статическом блоке класса: // static { int x = 1/0; } // При первой загрузке: ExceptionInInitializerError // При последующих попытках: NoClassDefFoundError (класс не был инициализирован)
AssertionError
Наследуется от Error. Выбрасывается оператором assert (при флаге -ea):
assert x > 0 : "x must be positive"; // AssertionError если x <= 0
Используйте assert для проверки инвариантов алгоритма, не для валидации входных данных API — для этого есть IllegalArgumentException.
StackOverflowError vs OutOfMemoryError
StackOverflowError vs OutOfMemoryError:
- StackOverflowError – исчерпан стек (глубокая рекурсия). Лечение: увеличить -Xss или исправить рекурсию
- OutOfMemoryError – исчерпана куча (много объектов). Лечение: увеличить -Xmx или исправить утечку
Senior Level
Обработка Error — почему плохая практика
Даже если поймали Error, JVM может находиться в нестабильном состоянии. Следующая операция (даже логирование) может вызвать новый Error.
Fail Fast & Die паттерн
В Highload-системах при Error приложение должно быстро завершиться:
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
if (e instanceof Error) {
log.error("Fatal error in thread {}", thread.getName(), e);
System.exit(1); // Умираем, чтобы оркестратор перезапустил
}
});
}
Kubernetes или другой оркестратор перезапустит свежий экземпляр.
JVM флаг для OOM
-XX:OnOutOfMemoryError="kill -9 %p"
Гарантирует смерть процесса при OutOfMemoryError.
StackOverflowError
Частый Error при бесконечной рекурсии. Размер стека ограничен -Xss. В глубоких иерархиях или при циклах в сущностях Hibernate может потребоваться увеличение.
// Бесконечная рекурсия
public int factorial(int n) {
return n * factorial(n); // StackOverflowError
}
Logging Errors
Если ловите Throwable на верхнем уровне (main или UncaughtExceptionHandler), делайте System.exit(1) после логирования, чтобы не оставить систему в “зомби-состоянии”.
Диагностика
javap -v— посмотрите атрибуты методов для понимания обработки исключений- Thread Dumps —
jstack <pid>покажет состояние всех потоков при зависании - Heap Dumps —
jmapпоможет анализироватьOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError— автоматический дамп кучи при OOM
🎯 Шпаргалка для интервью
Обязательно знать:
Error— проблемы JVM (невозможно исправить),Exception— ошибки приложения (можно обработать)- Error:
OutOfMemoryError,StackOverflowError,NoClassDefFoundError— не ловить - Exception делится на checked и unchecked (RuntimeException)
NoClassDefFoundError≠ClassNotFoundException: первый — Error (был при компиляции, нет в рантайме), второй — Exception (явно грузили, нет в classpath)AssertionError— технически Error, но из-за логики приложения (проверка инвариантов)- Fail Fast & Die: при Error —
System.exit(1), пусть оркестратор перезапустит - JVM флаг
-XX:OnOutOfMemoryError="kill -9 %p"гарантирует смерть при OOM Thread.setDefaultUncaughtExceptionHandler— перехватывает Error на верхнем уровне
Частые уточняющие вопросы:
- Можно ли поймать OutOfMemoryError? — Технически да, но JVM в нестабильном состоянии — следующая операция упадёт
- В чём разница между StackOverflowError и OutOfMemoryError? — StackOverflowError — исчерпан стек (рекурсия), OOM — исчерпана куча (много объектов)
- Когда AssertionError — это Error, а не Exception? — Потому что assert проверяет инварианты алгоритма, а не внешние сбои
- Что делать при Error в продакшне? — Залогировать и
System.exit(1), Kubernetes перезапустит под
Красные флаги (НЕ говорить):
- “Я ловлю Error и продолжаю работу” — JVM нестабильна, это бессмысленно
- “NoClassDefFoundError и ClassNotFoundException — одно и то же” — нет, первый Error, второй Exception
- “AssertionError используется для валидации входных данных” — для этого есть IllegalArgumentException
Связанные темы:
- [[Что находится в вершине иерархии исключений]]
- [[Что такое Throwable]]
- [[Что такое unchecked exception (Runtime Exception)]]
- [[Гарантируется ли выполнение блока finally]]
- [[Что такое stack trace]]