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

Какие исключения нужно обрабатывать обязательно?

Компилятор Java заставляет обрабатывать checked исключения. Это все наследники Exception, кроме RuntimeException.

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

Junior Level

Checked исключения — обязательная обработка

Компилятор Java заставляет обрабатывать checked исключения. Это все наследники Exception, кроме RuntimeException.

Checked exceptions – это контракт между методом и его вызывающим. Метод говорит: «Я могу столкнуться с X, и ты должен быть к этому готов.»

// Обязательно: либо try-catch, либо throws
public void readFile() throws IOException {
    Files.readAllLines(Paths.get("file.txt"));
}

// Либо обработка на месте
public void safeRead() {
    try {
        Files.readAllLines(Paths.get("file.txt"));
    } catch (IOException e) {
        log.error("Failed to read file", e);
    }
}

Распространённые исключения для обработки

  • IOException — работа с файлами, сетью
  • SQLException — работа с базой данных
  • ClassNotFoundException — загрузка классов
  • InterruptedException — многопоточность

Unchecked исключения — необязательная обработка

NullPointerException, IllegalArgumentException и другие RuntimeException можно не обрабатывать. Они сигнализируют об ошибках в коде.

Базовое правило

  • Checked — внешние сбоки (файлы, сеть, БД) → обязаны обработать
  • Unchecked — ошибки программирования → должны исправить код

Middle Level

Обязательная vs рекомендуемая обработка

Тип Обязанность Кто требует
Checked Обязан обработать или объявить throws Компилятор
Unchecked Рекомендуется обработать Архитектура/команда
Error Не должен перехватывать JVM (восстановление невозможно)

Компиляторная проверка

На уровне JVM разделения на checked и unchecked не существует. Инструкция athrow просто выбрасывает объект. Обязательность обработки — это только проверка javac.

Атрибут Exceptions в class-файле

При объявлении void method() throws IOException в .class файле появляется атрибут Exceptions. Компилятор использует его для проверки. Другие языки (Kotlin, Groovy) игнорируют этот атрибут.

Sneaky Throws — обход проверки

Можно выбросить checked исключение без throws:

@SuppressWarnings("unchecked")
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
    throw (E) e; // Стирание типов обманывает компилятор
}

// Lombok делает это автоматически
@SneakyThrows
public void readFile() {
    // IOException без throws!
    Files.readAllLines(Paths.get("file.txt"));
}

Exception Translation

Правильный подход — перехватывать checked исключения на границе слоя и переводить в доменные unchecked:

public interface Storage {
    byte[] read(String path); // Не зависит от SQL
}

public class SqlStorage implements Storage {
    public byte[] read(String path) {
        try {
            return jdbcTemplate.queryForObject(...);
        } catch (SQLException e) {
            throw new StorageException("Failed to read: " + path, e);
        }
    }
}

Senior Level

Функциональное программирование и checked exceptions

Стандартные интерфейсы (Function, Predicate, Supplier) не поддерживают checked исключения:

// Не скомпилируется
stream.map(path -> Files.readString(path))

// Решение 1 — обёртка
stream.map(path -> {
    try { return Files.readString(path); }
    catch (IOException e) { throw new UncheckedIOException(e); }
})

// Решение 2 — Sneaky Throws
stream.map(path -> sneakyRead(path))

Производительность

Никогда не используйте checked исключения для управления логикой. Бросание исключения — создание объекта + обход стека — в 100-1000 раз медленнее возврата Optional.

Валидация входных данных

Для невалидных данных всегда используйте unchecked исключения (IllegalArgumentException). Это ошибка программиста, а не внешнего мира.

InterruptedException – исключение, подтверждающее правило. Это не «control flow» в обычном смысле, а механизм кооперативной отмены потоков. Его обработка – часть контракта многопоточности.

UncaughtExceptionHandler

Если checked исключение “просочилось” через sneaky throws и не было поймано, оно дойдёт до обработчика потока. Установите глобальный обработчик:

Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
    log.error("Uncaught exception in thread {}", thread.getName(), e);
});

Empty Catch Blocks — худшее преступление

catch (IOException e) {} // Преступление!

Если обязаны обработать, но не знаете как — логируйте:

catch (IOException e) {
    log.error("IO error occurred", e);
    throw new RuntimeException(e);
}

Диагностика

  • Static Analysis — SonarQube блокирует пустые catch-блоки
  • Micrometer — отслеживайте количество исключений через метрики
  • Log Correlation — сохраняйте Trace ID для всех типов ошибок

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

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

  • Checked исключения — обязан обработать или объявить throws (компилятор требует)
  • Unchecked — рекомендуется обработать на уровне архитектуры (не компилятор)
  • Error — НЕ должен перехватываться (JVM в нестабильном состоянии)
  • На уровне JVM разделения нет — инструкция athrow выбрасывает всё одинаково
  • Sneaky throws — обход проверки компилятора через стирание типов
  • Exception translation — перехват checked на границе слоя → перевод в доменное unchecked
  • Для невалидных входных данных всегда использовать unchecked (IllegalArgumentException)
  • Пустые catch-блоки — худшее преступление: минимум логировать

Частые уточняющие вопросы:

  • Как JVM обрабатывает checked vs unchecked? — Одинаково; разделение только в javac
  • Что такое sneaky throws? — Обход проверки через @SneakyThrows (Lombok) или дженерик-стирание
  • Почему InterruptedException — особое? — Это механизм кооперативной отмены потоков, нужно восстановить статус прерывания
  • Что делать, если не знаешь как обработать? — Логировать и пробросить как RuntimeException

Красные флаги (НЕ говорить):

  • “Я оставляю пустой catch-блок, если не знаю что делать” — хотя бы логируйте
  • “Я ловлю Error, чтобы приложение не падало” — JVM нестабильна, это бессмысленно
  • “Checked исключения — это то же самое, что unchecked” — нет, компилятор проверяет только checked

Связанные темы:

  • [[Что такое checked exception и когда его использовать]]
  • [[Что такое unchecked exception (Runtime Exception)]]
  • [[В чём разница между Error и Exception]]
  • [[Почему не стоит глотать исключения (catch empty)]]
  • [[Что такое suppressed exceptions]]