【问题标题】:Is try-with-resource not safe when declaring multiple effectively final resources?声明多个有效的最终资源时,try-with-resource 是否不安全?
【发布时间】:2021-05-13 05:11:41
【问题描述】:

从 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()");
        }
    }

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

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

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

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

【问题讨论】:

    标签: java java-9 try-with-resources autocloseable


    【解决方案1】:

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

    try (r1; Resource2 r2 = new Resource2()) { // this causes r1 to close if r2 throws an exception
    

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

    【讨论】:

    • 对不起,我认为我写的问题可能给人的印象是我假设的。我不是。我只是想确保不应该发生某种魔法,而且由于某种原因它没有发生。您的回答清除了它,这与我的结论相同。谢谢。 :)
    • @RinaldoDev 不用担心,事实上,在重新阅读您的问题后,我明白了您试图在问题中提出的要点,这就是我在回答中添加第二段的原因。
    • 如果您想要一个新功能用例的实际示例,请考虑this answer。该方法返回由 JDBC 资源支持的 Stream。所以它不能使用普通的try(…),因为它会在 Stream 返回给调用者之前无条件地关闭资源。只有在错误的情况下,才应该在将异常传递给调用者之前安全地关闭所有资源。成功后,必须在 Stream 实例上注册一个 Runnable,以便在调用者关闭流时关闭已经存在的资源。
    • 该答案的代码使用UncheckedCloseable 表示已打开的资源并在两种情况下统一处理它们,在错误时立即关闭或在Stream 注册为Runnable 成功案子。注意该类型中的try(Resource dummy = existing) 模式;在 Java 9 之前就是这样,现在在 Java 9+ 中,我们可以写try(existing),它只与这种特殊情况相关。在所有其他情况下,try(Resource name = creation}expression) 应该像以前一样使用。
    【解决方案2】:

    为了使try-with-resources 机制工作,您应该将初始化代码放入try。您的初始化似乎发生在 1 行之前。如果你愿意:

      public static void main(String[] args) {
        try (Resource1 r1 = new Resource1(); Resource2 r2 = new Resource2()) {
          System.out.println("TryWithResources.main() try");
        } catch (Exception e) {
          System.out.println("TryWithResources.main() catch");
        }
      }
    

    会打印:

    TryWithResources.Resource1.close()
    TryWithResources.main() catch
    

    所以你的代码应该这样读:“我想自动关闭r1r2,但我更喜欢自己初始化”

    我的版本应该这样读:“我想自动关闭r1r2,并且我想将它初始化为try 块的一部分” p>

    【讨论】:

      猜你喜欢
      • 2016-08-18
      • 1970-01-01
      • 1970-01-01
      • 2019-04-05
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多