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

В чём разница между Error и Exception?

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

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

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 Dumpsjstack <pid> покажет состояние всех потоков при зависании
  • Heap Dumpsjmap поможет анализировать OutOfMemoryError
  • -XX:+HeapDumpOnOutOfMemoryError — автоматический дамп кучи при OOM

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

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

  • Error — проблемы JVM (невозможно исправить), Exception — ошибки приложения (можно обработать)
  • Error: OutOfMemoryError, StackOverflowError, NoClassDefFoundError — не ловить
  • Exception делится на checked и unchecked (RuntimeException)
  • NoClassDefFoundErrorClassNotFoundException: первый — 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]]