Java抑制异常

2023/05/30

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对象从未被初始化,所以当我们试图在finally块中关闭它时,它会抛出NullPointerException。我们的调用方法只会得到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

最后,让我们看一个使用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()");
    }
}

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

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上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章