Menu
Coddy logo textTech

Иерархия классов исключений

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 59 из 87.

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

В самом верху находится Throwable — родитель всех исключений и ошибок. У него есть два прямых подкласса:

Throwable
├── Error          // Серьёзные проблемы (не перехватывайте их)
└── Exception      // Восстанавливаемые проблемы
    └── RuntimeException  // Непроверяемые исключения

Ошибка представляет серьёзные проблемы, которые приложения не должны пытаться обрабатывать, например OutOfMemoryError или StackOverflowError. Они указывают на фундаментальные проблемы самой JVM.

Exception — это ветвь, с которой вы будете работать чаще всего. Она разделяется на две категории: проверяемые исключения (прямые подклассы Exception) и непроверяемые исключения (подклассы RuntimeException).

// Общие классы исключений
Exception
├── IOException           // Проблемы с файлами/сетью
├── SQLException          // Проблемы с базой данных
└── RuntimeException
    ├── NullPointerException
    ├── ArrayIndexOutOfBoundsException
    └── IllegalArgumentException

Поскольку exceptions являются классами, они следуют правилам наследования. Когда вы перехватываете родительский тип exception, вы также перехватываете всех его потомков:

try {
    // какой-то код
} catch (Exception e) {
    // Перехватывает ВСЕ исключения (обычно слишком широко)
}
try {
    // какой-то код
} catch (RuntimeException e) {
    // Catches only runtime exceptions and subclasses
}

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

challenge icon

Задание

Легко

Давайте создадим анализатор исключений, который на практике демонстрирует работу иерархии исключений Java! Вы создадите систему, которая перехватывает исключения на разных уровнях конкретности и сообщает, какой тип был перехвачен.

Вы организуете код в трёх файлах:

  • ExceptionThrower.java: Создайте class, который может вызывать разные типы исключений в зависимости от входных данных. В вашем class должны быть три статических метода:

    triggerRuntime(String type) — в зависимости от строки типа выбрасывайте разные исключения времени выполнения:

    • Если type равен "null", выбросьте NullPointerException с сообщением "Null value encountered"
    • Если type равен "index", выбросьте ArrayIndexOutOfBoundsException с сообщением "Invalid index"
    • Если type равен "argument", выбросьте IllegalArgumentException с сообщением "Bad argument"

    triggerArithmetic() — выбросьте ArithmeticException с сообщением "Division error"

    triggerGeneric() — выбросьте IllegalStateException с сообщением "Generic runtime error" (его иерархия: Exception > RuntimeException > IllegalStateException)

  • ExceptionAnalyzer.java: Создайте class, который перехватывает исключения на разных уровнях иерархии и определяет, что было перехвачено. Добавьте следующие статические методы:

    analyzeSpecific(String type) — вызовите ExceptionThrower.triggerRuntime(type) внутри блока try. Используйте отдельные блоки catch для NullPointerException, ArrayIndexOutOfBoundsException и IllegalArgumentException. Для каждого выведите: Caught specific: [exception class simple name], а затем Message: [exception message]

    analyzeWithParent() — вызовите ExceptionThrower.triggerArithmetic() внутри блока try. Перехватите его с помощью RuntimeException (родительского типа). Выведите: Caught via parent: RuntimeException, а затем Actual type: [exception class simple name]

    analyzeWithGrandparent() — вызовите ExceptionThrower.triggerGeneric() внутри блока try. Перехватите его с помощью Exception (ещё более высокого уровня в иерархии). Выведите: Caught via grandparent: Exception, а затем Actual type: [exception class simple name]

  • Main.java: Объедините ваш анализатор исключений! Вы получите одни входные данные: строку типа исключения (одно из "null", "index" или "argument").

    Сначала выведите === Specific Catch === и вызовите analyzeSpecific с вашими входными данными.

    Затем выведите пустую строку, === Parent Catch === и вызовите analyzeWithParent.

    Наконец, выведите пустую строку, === Grandparent Catch === и вызовите analyzeWithGrandparent.

Вы получите одни входные данные: тип исключения, которое нужно вызвать ("null", "index" или "argument").

Используйте e.getClass().getSimpleName(), чтобы получить имя class исключения, и e.getMessage(), чтобы получить его сообщение. Это задание показывает, как перехват родительского типа исключения также перехватывает всех его потомков: это ключевая концепция при выборе степени конкретности обработки исключений!

Попробуйте сами

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String type = scanner.nextLine();
        
        // TODO: Print "=== Specific Catch ===" and call ExceptionAnalyzer.analyzeSpecific(type)
        
        // TODO: Выведите пустую строку, затем "=== Parent Catch ===", затем вызовите ExceptionAnalyzer.analyzeWithParent()
        
        // TODO: Print a blank line, then "=== Grandparent Catch ===", then call ExceptionAnalyzer.analyzeWithGrandparent()
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Java