197. Java 异常 - Suppressed Exceptions:捕捉多个异常并保留它们

89 阅读3分钟

197. Java 异常 - Suppressed Exceptions:捕捉多个异常并保留它们


🧠 什么是 Suppressed Exceptions?

在 Java 的 try-with-resources 语法中,当多个异常同时发生时,JVM 会选择抛出其中一个异常,并将其他异常作为 “抑制的异常(suppressed exceptions)” 保存起来。这些被抑制的异常不会立刻抛出,但可以通过调用 Throwable.getSuppressed() 方法来访问。


🔍 如何抑制异常?

假设我们在 try-with-resources 语句中有多个资源,且其中某些资源的关闭操作可能抛出异常。如果 try 块本身也抛出了一个异常,那么关闭资源时抛出的异常会被“抑制”,并不会直接抛出。取而代之的是,try 块抛出的异常会作为主异常,其他异常会被抑制。

示例:

public static void writeToFileZipFileContents(String zipFileName, String outputFileName) throws IOException {
    Charset charset = StandardCharsets.US_ASCII;
    Path outputFilePath = Paths.get(outputFileName);

    try (
        ZipFile zf = new ZipFile(zipFileName);
        BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset)
    ) {
        for (Enumeration<? extends ZipEntry> entries = zf.entries(); entries.hasMoreElements();) {
            String entryName = entries.nextElement().getName() + System.lineSeparator();
            writer.write(entryName);
        }
    } catch (IOException e) {
        // 处理主要异常
        System.err.println("Exception occurred: " + e.getMessage());
        // 查看抑制的异常
        Throwable[] suppressedExceptions = e.getSuppressed();
        for (Throwable suppressed : suppressedExceptions) {
            System.err.println("Suppressed: " + suppressed.getMessage());
        }
        throw e; // 重新抛出主异常
    }
}

🔑 关键点

  1. 主异常:在 try 块中抛出的异常是“主异常”,最终会被抛出。
  2. 抑制异常:如果在 try-with-resources 语句中有多个资源关闭时抛出异常,这些异常会被抑制。
  3. 获取抑制异常:你可以通过 Throwable.getSuppressed() 方法来访问抑制的异常,查看它们的详细信息。

🧑‍🏫 示例:

  • 自定义两个资源 MyResource,它们在 close() 时都会抛异常;
  • try 块中也手动抛一个异常;
  • 最终看看 主异常抑制异常 是如何表现的。
class MyResource implements AutoCloseable {
    private final String name;

    public MyResource(String name) {
        this.name = name;
    }

    @Override
    public void close() throws Exception {
        throw new Exception("Error while closing resource: " + name);
    }
}

public class SuppressedExceptionDemo {
    public static void main(String[] args) {
        try (
            MyResource res1 = new MyResource("Resource-1");
            MyResource res2 = new MyResource("Resource-2")
        ) {
            // 在 try 块里抛出一个异常
            throw new Exception("Main exception from try block");
        } catch (Exception e) {
            System.err.println("Caught main exception: " + e.getMessage());

            // 打印被抑制的异常
            Throwable[] suppressed = e.getSuppressed();
            for (Throwable t : suppressed) {
                System.err.println("Suppressed: " + t.getMessage());
            }
        }
    }
}

说明:

  • 主异常Main exception from try block,它来自 try 块内部。

  • 抑制的异常 来自 close() 方法,这里有两个资源要关闭,所以两个异常被“抑制”了。

  • Java 保证了 try 块里的异常优先,其他异常不会丢失,而是通过 getSuppressed() 保存。


🚀 总结:

  • try-with-resources 中,多个异常可以同时发生。
  • 如果 try 块抛出了异常,其他来自资源关闭的异常会被抑制
  • 抑制的异常 可以通过 getSuppressed() 方法访问,帮助我们进行更全面的异常调试。

🧠 讲解小贴士:

  • “在处理资源关闭时,虽然多个异常可能同时发生,但我们可以通过 getSuppressed() 获取所有相关的异常。这样,即使程序继续执行,我们仍然能了解所有发生的问题。”
  • “确保你在捕获并处理主异常时,也关注抑制的异常。这有助于你全面了解代码执行中的所有异常。”