На какие группы делятся исключения в 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). Попытка их поймать часто бессмысленна.

Ответ 18+ 🔞

Давай разберём эту вашу ебучую иерархию исключений, а то народ путается, как в тёмном лесу, блядь.

Смотри, вся эта кухня делится на три главных лагеря, и отличия между ними — не просто так, а по самой что ни на есть семантике, то есть смыслу, сука.

Вот тебе таблица, чтобы сразу в глаза бросилось:

Группа От кого ноги растут Компилятор смотрит? Что это за зверь и примеры
Проверяемые (Checked) java.lang.Exception (но не его сынок RuntimeException) ДА, ёпта! Требует! Ошибки, от которых не застрахуешься, внешние.
Файл не найден (IOException), база данных сдохла (SQLException), класс пропал (ClassNotFoundException).
Непроверяемые (Unchecked / Runtime) java.lang.RuntimeException НЕТ, похуй! Косяки программиста, внутренние.
Ткнул в null (NullPointerException), аргумент кривой передал (IllegalArgumentException), за границы массива полез (ArrayIndexOutOfBoundsException).
Ошибки (Errors) java.lang.Error НЕТ, и даже не думай ловить! Жёсткий пиздец на уровне JVM.
Память кончилась (OutOfMemoryError), стек переполнился (StackOverflowError), виртуалка захлебнулась (VirtualMachineError).

А в чём, блядь, главный подвох?

Вся соль — в этом контракте, который компилятор выбивает из тебя железной дубиной, или нет.

  • Checked исключения — это как расписка, ёба. Если твой метод может такого наворотить, он обязан это крикнуть на весь мир в сигнатуре. А тот, кто его вызывает, — либо ловит, либо сам такую же расписку даёт. Иначе компилятор просто ебальник свернёт и не пропустит.

    // Без этого "throws IOException" — никуда, чувак. Компилятор устроит истерику.
    public String readFirstLine(String path) throws IOException {
        return Files.readAllLines(Paths.get(path)).get(0);
    }
    
    // И вызывающий код стоит перед выбором: либо поймать, либо тоже объявить.
    public void processFile() {
        try {
            String line = readFirstLine("file.txt");
        } catch (IOException e) { // Ловим, блядь, как кот мышь
            System.err.println("Файл-то, сука, не читается!");
        }
    }
  • Unchecked исключения — это уже твои личные косяки. Компилятор на них смотрит, как на говно: "Ну, если уж ты, долбоёб, написал кривой код, который падает с NPE — твои проблемы, я не нянька". Никаких throws не нужно.

    // Смотри, какой коварный метод. Может выстрелить в ногу, но молчит как партизан.
    public int getLength(String str) {
        return str.length(); // Спокойненько так... И БАМ! NullPointerException, если str == null
    }

А про Errors зачем тогда? А зачем, блядь, ловить OutOfMemoryError? Это ж не ошибка в твоём коде — это JVM дохнет! Это как пытаться тушить пожар в доме, когда у тебя самого уже ноги оторвало. Бессмысленно, ёпта. Обычно на них просто забивают хуй и дают приложению сдохнуть с честью.