Иерархия классов исключений
Часть раздела Объектно-ориентированное программирование путешествия по 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
}Эта иерархия позволяет обрабатывать ошибки настолько конкретно или обобщённо, насколько это необходимо, и служит основой для создания собственных пользовательских исключений.
Задание
ЛегкоДавайте создадим анализатор исключений, который на практике демонстрирует работу иерархии исключений 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)- Если type равен
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()
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка10Обработка исключений в ООП
Иерархия классов исключенийПользовательские исключенияПроверяемые и непроверяемые ошибкиПаттерн Try With ResourcesПовторение — Validated User2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java