На какие группы делятся исключения в Java и в чём их ключевое отличие?

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

Ответ

Исключения в Java делятся на три фундаментальные группы, определяемые их классом-предком и семантикой обработки.

Группа Класс-предок Обработка компилятором Семантика / Примеры
Проверяемые (Checked) java.lang.Exception (но не RuntimeException) Требует обработки (try-catch) или объявления (throws) Восстанавливаемые ошибки внешней логики.
IOException, SQLException, ClassNotFoundException.
Непроверяемые (Unchecked / Runtime) java.lang.RuntimeException Не требует обязательной обработки Ошибки программирования или внутренней логики.
NullPointerException, IllegalArgumentException, ArrayIndexOutOfBoundsException.
Ошибки (Errors) java.lang.Error Не требует (и обычно не обрабатывается) Критические системные сбои JVM.
OutOfMemoryError, StackOverflowError, VirtualMachineError.

Ключевое отличие: Обязательность обработки

Различие между Checked и Unchecked исключениями — это контракт, обеспечиваемый компилятором.

  • Checked исключения — часть сигнатуры метода. Если метод может выбросить IOException, он должен объявить это. Вызывающий код вынужден решить: обработать исключение или передать его дальше.

    // Метод ОБЯЗАН объявить checked исключение
    public String readFirstLine(String path) throws IOException {
        return Files.readAllLines(Paths.get(path)).get(0);
    }
    
    // Вызывающий код ОБЯЗАН выбрать: обработать или объявить throws
    public void processFile() {
        try {
            String line = readFirstLine("file.txt");
        } catch (IOException e) { // ОБРАБОТАТЬ
            System.err.println("Не удалось прочитать файл");
        }
    }
  • Unchecked исключения не являются частью контракта метода. Их возникновение часто свидетельствует о баге (передан null, неверный индекс). Компилятор не заставляет вас их обрабатывать, так как корректный код не должен их вызывать.

    // Никаких throws не требуется, даже если метод может выбросить NPE
    public int getLength(String str) {
        return str.length(); // Возможен NullPointerException (unchecked)
    }

Почему Errors не обрабатывают? Ошибки типа OutOfMemoryError указывают на проблемы, с которыми приложение обычно не может осмысленно справиться (не хватает памяти всей JVM). Попытка их поймать часто бессмысленна.