Java禁止的异常

Java Suppressed Exceptions

1.简介

在本快速教程中,我们将学习Java中的抑制异常。简而言之,抑制异常是引发但被忽略的异常。在Java中,常见的情况是finally块引发异常。然后抑制最初在try块中引发的任何异常。

从Java 7开始,我们现在可以在Throwable类上使用两种方法来处理被抑制的异常:addSuppressed和getSuppressed。我们应该注意,try-with-resources构造也是Java 7中引入的。我们将在示例中看到它们之间的关系。

2.行动中被抑制的异常

2.1。抑制异常情况

让我们开始快速看一个示例,该示例通过在finally块中发生的异常来抑制原始异常:

1
2
3
4
5
6
7
8
9
10
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();
    }
}

只要我们提供现有文件的路径,就不会引发任何异常,并且该方法将按预期工作。

但是,假设我们提供了一个不存在的文件:

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

在这种情况下,try块尝试打开不存在的文件时将抛出FileNotFoundException。因为fileIn对象从未被初始化,所以当我们尝试在finally块中关闭它时,它将抛出NullPointerException。我们的调用方法只会得到NullPointerException,而最初的问题将是不明显的:该文件不存在。

2.2。添加抑制的异常

现在让我们看一下如何利用Throwable.addSuppressed方法提供原始异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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是如何工作的:

1
2
3
4
5
6
7
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的资源:

1
2
3
4
5
6
7
8
9
10
11
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()");
    }
}

接下来,让我们在try-with-resources块中使用ExceptionalResource:

1
2
3
4
5
public static void demoExceptionalResource() throws Exception {
    try (ExceptionalResource exceptionalResource = new ExceptionalResource()) {
        exceptionalResource.processSomething();
    }
}

最后,让我们继续进行单元测试,看看异常如何消除:

1
2
3
4
5
6
7
8
9
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上获得。