Для чего в Java используются непроверяемые исключения (unchecked exceptions)?

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

Ответ

Непроверяемые исключения (наследники RuntimeException и Error) используются для ошибок, которые, как правило, указывают на логические ошибки в программе или фатальные системные сбои. Их ключевая особенность — компилятор не требует их обязательного объявления в сигнатуре метода (throws) или обработки в блоке try-catch.

Основные причины использования:

  1. Для ошибок, которые невозможно или нецелесообразно обрабатывать локально. Например, NullPointerException, ArrayIndexOutOfBoundsException или IllegalArgumentException часто возникают из-за ошибок в логике программы, и их проще предотвратить, чем обрабатывать.
  2. Для сохранения чистоты API. Если бы каждое такое исключение нужно было объявлять, сигнатуры методов были бы перегружены throws-клаузами, что ухудшило бы читаемость.
  3. Для фатальных ошибок, после которых восстановление невозможно. Например, OutOfMemoryError или StackOverflowError (наследники Error) — непроверяемые, так как программа обычно не может их корректно обработать.

Пример:

public int getElement(int[] array, int index) {
    // Непроверяемое исключение. Предполагается, что вызывающий код
    // обеспечивает корректные index и array.
    return array[index]; // Может выбросить ArrayIndexOutOfBoundsException
}

public void setAge(int age) {
    if (age < 0) {
        // Логическая ошибка в аргументе — выбрасываем непроверяемое исключение.
        throw new IllegalArgumentException("Возраст не может быть отрицательным");
    }
    this.age = age;
}

Итог: Непроверяемые исключения применяются для ошибок, связанных с нарушением контракта кода (пред- и постусловий), где ответственность за их предотвращение лежит на вызывающей стороне, а не на механизме принудительной обработки.