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

Подавленные исключения Java

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

1. Введение

В этом кратком руководстве мы узнаем о подавленных исключениях в Java. Короче говоря, подавленное исключение — это исключение, которое выдается, но каким-то образом игнорируется. Обычный сценарий для этого в Java — когда блок finally генерирует исключение. Любое исключение, первоначально созданное в блоке try , затем подавляется.

Начиная с Java 7, теперь мы можем использовать два метода класса Throwable для обработки подавленных исключений: addSuppressed и getSuppressed . Следует отметить, что конструкция try-with-resources также была введена в Java 7. В наших примерах мы увидим, как они связаны.

2. Подавленные исключения в действии

2.1. Сценарий подавленного исключения

Давайте начнем с быстрого рассмотрения примера, в котором исходное исключение подавлено исключением, возникающим в блоке finally :

public static void demoSuppressedException(String filePath) throws IOException {
FileInputStream fileIn = null;
try {
fileIn = new FileInputStream(filePath);
} catch (FileNotFoundException e) {
throw new IOException(e);
} finally {
fileIn.close();
}
}

Пока мы указываем путь к существующему файлу, исключений не будет, и метод будет работать так, как ожидалось.

Однако предположим, что мы предоставляем несуществующий файл:

@Test(expected = NullPointerException.class)
public void givenNonExistentFileName_whenAttemptFileOpen_thenNullPointerException() throws IOException {
demoSuppressedException("/non-existent-path/non-existent-file.txt");
}

В этом случае блок try выдаст исключение FileNotFoundException при попытке открыть несуществующий файл. Поскольку объект fileIn никогда не был инициализирован, он выдаст исключение NullPointerException , когда мы попытаемся закрыть его в нашем блоке finally . Наш вызывающий метод получит только NullPointerException , и не будет сразу очевидно, в чем заключалась первоначальная проблема: файл не существует.

2.2. Добавление подавленного исключения

Теперь давайте посмотрим, как мы можем использовать метод Throwable.addSuppressed для создания исходного исключения:

public static void demoAddSuppressedException(String filePath) throws IOException {
Throwable firstException = null;
FileInputStream fileIn = null;
try {
fileIn = new FileInputStream(filePath);
} catch (IOException e) {
firstException = e;
} finally {
try {
fileIn.close();
} catch (NullPointerException npe) {
if (firstException != null) {
npe.addSuppressed(firstException);
}
throw npe;
}
}
}

Давайте перейдем к нашему модульному тесту и посмотрим, как работает getSuppressed в этой ситуации:

try {
demoAddSuppressedException("/non-existent-path/non-existent-file.txt");
} catch (Exception e) {
assertThat(e, instanceOf(NullPointerException.class));
assertEquals(1, e.getSuppressed().length);
assertThat(e.getSuppressed()[0], instanceOf(FileNotFoundException.class));
}

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

2.3. Использование попытки с ресурсами

Наконец, давайте рассмотрим пример использования try-with-resources , где метод close создает исключение. В Java 7 появилась конструкция try-with-resources и интерфейс AutoCloseable для управления ресурсами.

Во-первых, давайте создадим ресурс, реализующий AutoCloseable :

public class ExceptionalResource implements AutoCloseable {

public void processSomething() {
throw new IllegalArgumentException("Thrown from processSomething()");
}

@Override
public void close() throws Exception {
throw new NullPointerException("Thrown from close()");
}
}

Далее давайте используем наш ExceptionalResource в блоке try-with-resources :

public static void demoExceptionalResource() throws Exception {
try (ExceptionalResource exceptionalResource = new ExceptionalResource()) {
exceptionalResource.processSomething();
}
}

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

try {
demoExceptionalResource();
} catch (Exception e) {
assertThat(e, instanceOf(IllegalArgumentException.class));
assertEquals("Thrown from processSomething()", e.getMessage());
assertEquals(1, e.getSuppressed().length);
assertThat(e.getSuppressed()[0], instanceOf(NullPointerException.class));
assertEquals("Thrown from close()", e.getSuppressed()[0].getMessage());
}

Следует отметить, что при использовании AutoCloseable подавляется исключение `` , вызываемое методом close . Выбрасывается исходное исключение.

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

В этом коротком руководстве мы узнали, что такое подавленные исключения и как они возникают. Затем мы увидели, как использовать методы addSuppressed и getSuppressed для доступа к этим подавленным исключениям. Наконец, мы увидели, как работают подавленные исключения при использовании блока try-with-resources .

Как всегда, код примера доступен на GitHub .