Перейти к основному содержимому

Цепочки исключений в Java

· 4 мин. чтения

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, поэтому его легко импортировать и запускать как есть.