声明多个有效的最终资源时,try-with-resource 不安全吗?

Rin*_*PJr 2 java try-with-resources autocloseable java-9

从 Java 9 开始,我们可以在 try-with-resources 中有效地使用最终变量。

下面的示例展示了一种资源初始化引发异常的情况。

    public static void main(String[] args) {
        Resource1 r1 = new Resource1();
        Resource2 r2 = new Resource2(); // exception will be thrown
        try (r1; r2) {
            System.out.println("TryWithResources.main() try");
        } catch (Exception e) {
            System.out.println("TryWithResources.main() catch");
        }
    }
    
    static class Resource1 implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("TryWithResources.Resource1.close()");
        }
    }
    
    static class Resource2 implements AutoCloseable {
        public Resource2() {
            throw new RuntimeException();
        }
        @Override
        public void close() throws Exception {
            System.out.println("TryWithResources.Resource2.close()");
        }
    }
Run Code Online (Sandbox Code Playgroud)

当我运行这个例子时,我得到的唯一输出是一个 RuntimeException,这意味着 Resource1 没有关闭。这是意料之中的,因为它没有在 try-with-resources 中初始化。

但是,这是预期的结果,还是我错过了什么?

因为,如果这实际上是它应该工作的方式,那么在我看来,这种新语法实际上消除了最初由 try-with-resources 语句带来的许多安全性。

有人可以确认是否真的如此吗?而且,如果是肯定的,我们为什么要在多种资源中使用这种语法并承担这种风险?

man*_*uti 6

我认为您假设“初始化”发生在try语句中。唯一的例外是通过构造函数抛出之前try-with-resources到达。换句话说,该行try (r1; r2) {本身并没有初始化资源,它只是将它们称为变量。它与以try块为单位初始化资源不同:

try (r1; Resource2 r2 = new Resource2()) { // this causes r1 to close if r2 throws an exception
Run Code Online (Sandbox Code Playgroud)

话虽如此,您的观点是正确的,即新语法(访问最终变量)提供了灵活性,但代价是可能无法关闭先前创建的资源(如您的情况所示)。就我个人而言,我从来没有理由使用这种新语法。我想不出没有在try语句中创建资源的充分理由,毕竟在资源关闭后使用它是没有意义的。

  • 如果您想要新功能的用例的实际示例,请考虑[此答案](/sf/answers/2256252141/)。该方法返回由 JDBC 资源支持的“Stream”。因此它不能使用普通的“try(…)”,因为这会在 Stream 返回给调用者之前无条件关闭资源。仅在错误情况下,才应在将异常传递给调用者之前安全关闭所有资源。成功后,必须在“Stream”实例上注册“Runnable”,以便在调用者关闭流时关闭已存在的资源。 (3认同)