在扩展的资源尝试语句中究竟捕获了什么



在下面的代码块中:

try ( /* resources declaration */ ) {
    // some dangerous code
} catch (Exception e) {
    // error handling and reporting
}

如果 try 块中的代码和自动 close() 语句引发异常,会发生什么情况?哪一个会被困在catch块中?他们两个?只有一个?如果是,是哪一个?

此外,如果try成功但close不成功怎么办?会输入捕获块吗?

引用

JLS第14.20.3.1节:

在管理单个资源的基本 try-with-resources 语句中:

  • 如果资源的初始化由于值V throw而突然完成,则 try-with-resources 语句会因为值V throw而突然完成。
  • 如果资源的初始化正常完成,并且try块由于值Vthrow而突然完成,则:

    • 如果资源的自动关闭正常完成,则 try-with-resources 语句会因为值 V throw而突然完成。

    • 如果资源的自动关闭由于值V2throw而突然完成,则 try-with-resources 语句会突然完成,因为throw的值V V2添加到禁止的异常列表中 V

  • 如果资源的初始化正常完成,try块正常完成,并且资源的自动关闭因为抛出一个值V而突然完成,则 try-with-resources 语句会因为值V throw而突然完成。

这意味着,如果 try 块中的代码和自动 close() 语句都抛出异常,则 catch 部分将处理 try 块引发的异常,而抑制异常中close()抛出的异常。

此外,这意味着如果try块成功但自动close()失败,则catch将被执行,捕获的异常将是close()抛出的异常。


下面是验证此行为的测试:

public class Main {
    public static void main(String[] args) throws Exception {
        // try block fails and close() fails
        try (T t = new T()) {
            throw new Exception("thrown by try part");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println(e.getSuppressed()[0].getMessage());
        }
        // try block is successful but close() fails
        try (T t = new T()) {
            //
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
class T implements AutoCloseable {
    @Override
    public void close() throws Exception {
        throw new Exception("thrown by close");
    }
}

此代码将打印

thrown by try part
thrown by close
thrown by close

这意味着捕获的异常是第一部分代码的 try 部分引发的异常。对于第二部分,捕获的异常确实是close()抛出的异常。

try 块内抛出的异常被抛出到外部世界。

而使用 try-catch-finally 时,从 finally 块抛出的异常将沿调用堆栈向上传播。

InputStream input = null;
    try {
        input = new FileInputStream("file.txt");
        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    } finally {
        if(input != null){
            input.close();
        }
    }

在这种情况下,如果 try 块和 finally 块(当 InputStream 关闭时)中都发生异常,则会抛出最后一个异常,即使从 try 块抛出的异常可能与传播更相关。

try(FileInputStream input = new FileInputStream("file.txt")) {
        int data = input.read();
        while(data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }

在这种情况下,如果 try 块和 finally 块中同时发生异常,则会传播第一个

最新更新