1. Обзор
В этой статье мы очень кратко рассмотрим, что такое Exception
, и углубимся в обсуждение связанных исключений в Java.
Проще говоря, исключение
— это событие, нарушающее нормальный ход выполнения программы. Давайте теперь посмотрим, как именно мы можем сцепить исключения, чтобы получить от них лучшую семантику.
2. Цепочки исключений
Цепочка исключений
помогает определить ситуацию, в которой одно исключение вызывает другое исключение
в приложении.
Например, рассмотрим метод, который выдает ArithmeticException
из-за попытки деления на ноль, но фактической причиной исключения была ошибка ввода-вывода, из-за которой делитель был равен нулю. Метод выдает ArithmeticException
вызывающей стороне. Вызывающий не будет знать о фактической причине Exception
. В таких ситуациях используется Chained Exception .
Эта концепция была представлена в JDK 1.4.
Давайте посмотрим, как цепочечные исключения поддерживаются в Java.
3. Метательный
класс
Класс Throwable
имеет несколько конструкторов и методов для поддержки связанных исключений. Во-первых, давайте посмотрим на конструкторы.
Throwable (причина Throwable)
—
Throwable
имеет единственный параметр, который указывает фактическую причинуException
.Throwable(String desc, Throwable Cause)
—
этот конструктор также принимает описаниеException с фактической причиной
Exception
.
Далее, давайте посмотрим на методы, которые предоставляет этот класс:
Метод getCause()
— этот метод возвращает фактическую причину, связанную с текущимException
.- `
Метод
initCause()` — устанавливает основную причину, вызываяException
.
4. Пример
Теперь давайте посмотрим на пример, где мы установим собственное описание Exception
и создадим цепочку Exception
:
public class MyChainedException {
public void main(String[] args) {
try {
throw new ArithmeticException("Top Level Exception.")
.initCause(new IOException("IO cause."));
} catch(ArithmeticException ae) {
System.out.println("Caught : " + ae);
System.out.println("Actual cause: "+ ae.getCause());
}
}
}
Как предполагается, это приведет к:
Caught: java.lang.ArithmeticException: Top Level Exception.
Actual cause: java.io.IOException: IO cause.
5. Почему цепные исключения?
Нам нужно связать исключения, чтобы сделать журналы читабельными. Напишем два примера. Во-первых, без цепочки исключений, а во-вторых, со связанными исключениями. Позже мы сравним поведение журналов в обоих случаях.
Для начала создадим серию исключений:
class NoLeaveGrantedException extends Exception {
public NoLeaveGrantedException(String message, Throwable cause) {
super(message, cause);
}
public NoLeaveGrantedException(String message) {
super(message);
}
}
class TeamLeadUpsetException extends Exception {
// Both Constructors
}
Теперь давайте начнем использовать приведенные выше исключения в примерах кода.
5.1. Без цепочки
Давайте напишем пример программы, не связывая наши пользовательские исключения.
public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}
void getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
e.printStackTrace();
throw new NoLeaveGrantedException("Leave not sanctioned.");
}
}
void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team Lead Upset");
}
}
В приведенном выше примере логи будут выглядеть так:
com.foreach.chainedexception.exceptions.TeamLeadUpsetException:
Team lead Upset
at com.foreach.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:46)
at com.foreach.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
at com.foreach.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Exception in thread "main" com.foreach.chainedexception.exceptions.
NoLeaveGrantedException: Leave not sanctioned.
at com.foreach.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:37)
at com.foreach.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
5.2. С цепочкой
Далее давайте напишем пример с цепочкой наших пользовательских исключений:
public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}
public getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
throw new NoLeaveGrantedException("Leave not sanctioned.", e);
}
}
public void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team lead Upset.");
}
}
Наконец, давайте посмотрим на логи, полученные с помощью цепочки исключений:
Exception in thread "main" com.foreach.chainedexception.exceptions
.NoLeaveGrantedException: Leave not sanctioned.
at com.foreach.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:36)
at com.foreach.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Caused by: com.foreach.chainedexception.exceptions
.TeamLeadUpsetException: Team lead Upset.
at com.foreach.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:44)
at com.foreach.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
... 1 more
Мы можем легко сравнить показанные журналы и сделать вывод, что цепочка исключений приводит к более чистым журналам.
6. Заключение
В этой статье мы рассмотрели концепцию связанных исключений.
Реализацию всех примеров можно найти в проекте Github — это проект на основе Maven, поэтому его легко импортировать и запускать как есть.