В чому різниця між 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 виняток (Runtime Exception)]]
- [[Чи завжди виконується блок finally]]
- [[Що таке stack trace]]