Какова иерархия классов ошибок и исключений в Java?

«Какова иерархия классов ошибок и исключений в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Java все ошибки и исключения организованы в иерархию, корнем которой является класс Throwable. Понимание этой иерархии критично для корректной обработки ошибок.

graph TD
    A[Throwable] --> B[Error]
    A --> C[Exception]

    B --> B1[VirtualMachineError]
    B1 --> B2[OutOfMemoryError]
    B1 --> B3[StackOverflowError]
    B --> B4[LinkageError]

    C --> D[IOException]
    D --> D1[FileNotFoundException]
    C --> E[RuntimeException]
    E --> E1[NullPointerException]
    E --> E2[IllegalArgumentException]
    E --> E3[IndexOutOfBoundsException]
    C --> F[Прочие проверяемые исключения<br/>например, SQLException]

Ключевые ветви иерархии:

  1. Error и его подклассы:

    • Представляют критические системные ошибки, от которых приложение обычно не может восстановиться (например, нехватка памяти, сбой JVM).
    • Обрабатывать их, как правило, не нужно и не рекомендуется.
    • Примеры: OutOfMemoryError, StackOverflowError, NoClassDefFoundError.
  2. Exception и его подклассы:

    • Представляют исключительные ситуации, с которыми приложение может и должно уметь работать.

    • Делится на две основные категории:

    • Проверяемые исключения (Checked Exceptions):

      • Наследуются от Exception, но НЕ от RuntimeException.
      • Компилятор требует их обработки (через try-catch) или объявления в сигнатуре метода (throws).
      • Примеры: IOException, SQLException, ClassNotFoundException.
      • Используются для ожидаемых ошибок внешних условий (файл не найден, сетевой сбой).
    • Непроверяемые исключения (Unchecked Exceptions):

      • Наследуются от RuntimeException.
      • Компилятор не проверяет их обработку.
      • Примеры: NullPointerException, IllegalArgumentException, ArrayIndexOutOfBoundsException.
      • Обычно указывают на ошибки программирования (баги в коде).

Пример обработки с учётом иерархии:

try {
    // Код, который может выбросить разные исключения
    someRiskyOperation();
} catch (FileNotFoundException e) {
    // Обработка конкретного проверяемого исключения
    System.out.println("Файл не найден: " + e.getMessage());
} catch (IOException e) {
    // Более общая обработка всех IO-исключений
    System.out.println("Ошибка ввода-вывода: " + e.getMessage());
} catch (IllegalArgumentException e) {
    // Обработка непроверяемого исключения
    System.out.println("Неверный аргумент: " + e.getMessage());
} catch (Exception e) {
    // Универсальный обработчик для всех оставшихся Exception
    System.out.println("Произошла ошибка: " + e.getMessage());
} // Error-ы здесь не ловятся, и это правильно.