Menu
Coddy logo textTech

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

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

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

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

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

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

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

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

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

try {
    // некоторый код
} catch (Exception e) {
    // Перехватывает ВСЕ исключения (обычно слишком широко)
}
try {
    // некоторый код
} catch (RuntimeException e) {
    // Перехватывает только исключения времени выполнения и их подклассы
}

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

challenge icon

Задание

Легко

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

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

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

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

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

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

    triggerGeneric() — выбросьте RuntimeException с сообщением "Generic runtime error"

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

    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(), чтобы получить имя класса исключения, и e.getMessage(), чтобы получить его сообщение. Эта задача показывает, как перехват родительского типа исключения также перехватывает все его дочерние типы — ключевая концепция при принятии решения о том, насколько специфичной должна быть ваша обработка исключений!

Шпаргалка

Обработка исключений в Java построена на иерархии классов, корнем которой является Throwable:

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

Error представляет собой серьезные проблемы JVM, такие как OutOfMemoryError или StackOverflowError, которые приложения не должны обрабатывать.

Exception делится на проверяемые исключения (прямые подклассы) и непроверяемые исключения (подклассы RuntimeException):

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

Перехват родительского типа исключения также перехватывает все его дочерние типы:

try {
    // какой-то код
} catch (Exception e) {
    // Перехватывает ВСЕ исключения
}

try {
    // какой-то код
} catch (RuntimeException e) {
    // Перехватывает только исключения времени выполнения и их подклассы
}

Используйте e.getClass().getSimpleName(), чтобы получить имя класса исключения, и 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: Выведите "=== Specific Catch ===" и вызовите ExceptionAnalyzer.analyzeSpecific(type)
        
        // TODO: Выведите пустую строку, затем "=== Parent Catch ===", а затем вызовите ExceptionAnalyzer.analyzeWithParent()
        
        // TODO: Выведите пустую строку, затем "=== Grandparent Catch ===", а затем вызовите ExceptionAnalyzer.analyzeWithGrandparent()
    }
}
quiz iconПроверьте себя

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

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