扩展的 try-with-resources 语句到底捕获了什么?

What exactly gets caught in an extended try-with-resources statement?

在下面的代码块中:

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

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

此外,如果try成功而close不成功怎么办?会进入 catch 块吗?

引自 JLS 部分 14.20.3.1:

In a basic try-with-resources statement that manages a single resource:

  • If the initialization of the resource completes abruptly because of a throw of a value V, then the try-with-resources statement completes abruptly because of a throw of the value V.
  • If the initialization of the resource completes normally, and the try block completes abruptly because of a throw of a value V, then:

    • If the automatic closing of the resource completes normally, then the try-with-resources statement completes abruptly because of a throw of the value V.

    • If the automatic closing of the resource completes abruptly because of a throw of a value V2, then the try-with-resources statement completes abruptly because of a throw of value V with V2 added to the suppressed exception list of V.

  • If the initialization of the resource completes normally, and the try block completes normally, and the automatic closing of the resource completes abruptly because of a throw of a value V, then the try-with-resources statement completes abruptly because of a throw of the value V.

这意味着如果 try 块内的代码和自动 close() 语句都抛出异常,catch 部分将处理 [=16] 抛出的异常=] 块,suppressed exceptions.

中的 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 块中都发生异常,则传播第一个异常