1. Обзор

В этой статье мы очень кратко рассмотрим, что такое Exception, и углубимся в обсуждение связанных исключений в Java.

Проще говоря, исключение — это событие, нарушающее нормальный ход выполнения программы. Давайте теперь посмотрим, как именно мы можем сцепить исключения, чтобы получить от них лучшую семантику.

2. Цепочка исключений

Цепочка исключений помогает определить ситуацию, в которой одно исключение вызывает другое исключение в приложении.

Например, рассмотрим метод, который выдает исключение ArithmeticException из-за попытки деления на ноль, но фактической причиной исключения была ошибка ввода-вывода, из-за которой делитель был равен нулю. Метод выдает исключение ArithmeticException вызывающей стороне. . Вызывающий не будет знать о фактической причине исключения. В таких ситуациях используется Chained Exception.

Эта концепция была представлена ​​в JDK 1.4.

Давайте посмотрим, как в Java поддерживаются связанные исключения.

3. Класс Throwable

Класс Throwable имеет несколько конструкторов и методов для поддержки связанных исключений. Во-первых, давайте посмотрим на конструкторы.

Throwable (причина Throwable) — Throwable имеет единственный параметр, который указывает фактическую причину исключения. Throwable(String desc, Throwable Cause) — этот конструктор также принимает описание Exception с фактической причиной Exception.

    Далее давайте посмотрим на методы, предоставляемые этим классом:

Метод getCause() — этот метод возвращает фактическую причину, связанную с текущим Исключением. Метод initCause () — устанавливает основную причину с вызовом исключения.

    4. Пример

Теперь давайте рассмотрим пример, в котором мы установим собственное описание исключения и создадим связанное исключение:

Как вы уже догадались, это приведет к:

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());
        }
    }    
}

5. Почему цепные исключения?

Caught: java.lang.ArithmeticException: Top Level Exception.
Actual cause: java.io.IOException: IO cause.

Нам нужно связать исключения в цепочку, чтобы сделать журналы читабельными. Напишем два примера. Во-первых, без цепочки исключений, а во-вторых, со связанными исключениями. Позже мы сравним поведение журналов в обоих случаях.

Для начала мы создадим ряд исключений:

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

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");
    }
}

5.2. С цепочкой

com.baeldung.chainedexception.exceptions.TeamLeadUpsetException: 
  Team lead Upset
    at com.baeldung.chainedexception.exceptions.MainClass
      .howIsTeamLead(MainClass.java:46)
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:34)
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29)
Exception in thread "main" com.baeldung.chainedexception.exceptions.
  NoLeaveGrantedException: Leave not sanctioned.
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:37)
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29)

Далее давайте напишем пример с цепочкой наших пользовательских исключений:

Наконец, давайте посмотрим на журналы, полученные с помощью цепочек исключений:

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.baeldung.chainedexception.exceptions
  .NoLeaveGrantedException: Leave not sanctioned. 
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:36) 
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29) 
Caused by: com.baeldung.chainedexception.exceptions
  .TeamLeadUpsetException: Team lead Upset.
    at com.baeldung.chainedexception.exceptions.MainClass
  .howIsTeamLead(MainClass.java:44) 
    at com.baeldung.chainedexception.exceptions.MainClass
  .getLeave(MainClass.java:34) 
    ... 1 more

6. Заключение

В этой статье мы рассмотрели концепцию связанных исключений.

Реализацию всех примеров можно найти в проекте Github — это проект на основе Maven, поэтому его легко импортировать и запускать как есть.

«