Иерархия классов исключений
Часть раздела Объектно-ориентированное программирование путешествия по 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) {
// Перехватывает только исключения времени выполнения и их подклассы
}Эта иерархия позволяет вам быть настолько конкретными или общими, насколько это необходимо при обработке ошибок, и она является основой для создания ваших собственных пользовательских исключений.
Задание
ЛегкоДавайте создадим анализатор исключений, который на практике демонстрирует работу иерархии исключений в 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()
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
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
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»