【问题标题】:Will the single resource in try with resource statement be not closed if there is an exception thrown from the statement (initialization issue)如果语句抛出异常(初始化问题),try with resource语句中的单个资源是否不会关闭
【发布时间】:2018-11-01 03:05:43
【问题描述】:

示例代码:

    class TestCharStream {
        public static void main(String[] args){
// Assume specified file is not available in the location
            try (Reader reader = new FileReader("C:\\TestData\\test123.txt")) {
                System.out.println("Entered Try block");
                int content;
                while ((content = reader.read()) != -1) {
                    System.out.print((char) content);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

由于文件不可用,会抛出'FileNotFoundException',资源初始化失败。

根据 Java SE7 规范,我了解以下与“使用资源尝试”声明相关的要点

  • 如果资源初始化失败(即其初始化表达式抛出异常),那么到目前为止由 try-with-resources 语句初始化的所有资源都将关闭。
  • 只有在初始化为非空值时才关闭资源。
  • 如果try块和try-with-resources语句都抛出异常,则该方法抛出try块抛出的异常; try-with-resources 语句抛出的异常被抑制。

我有关于关闭资源、抑制与上述要点相关的异常的问题

1) 上述示例代码案例(即资源初始化失败)是否不属于场景..无论try语句正常完成还是突然完成,资源都会关闭。是不是只适用于在try with resources语句中初始化成功且try块有错误的资源?

2) 如果在初始化资源时抛出异常(如示例),这将属于初始化不成功的情况。在这种情况下,我们怎么会遇到 try-with-resources 语句中抛出异常的情况(基本上只能在初始化期间发生)?是关闭资源时抛出的异常吗?

【问题讨论】:

    标签: java try-with-resources


    【解决方案1】:

    1) 上面的示例代码情况(即资源失败 初始化)不属于场景..资源将被关闭 无论 try 语句是否正常完成或 突然。这是否仅适用于初始化的资源 成功尝试使用资源语句,但出现错误 try 块?

    不清楚您所引用的“资源将被关闭,无论 try 语句是正常完成还是突然完成”。然而,如果在资源规范中尝试初始化资源会引发异常,那么不,该资源不会关闭,因为它没有被初始化为非空值(您的第二个要点,这是 JLS 文本的摘录) .

    如果资源规范中有多个资源,那么有可能在其中一个引发的初始化之前初始化一些资源;在这种情况下,那些成功初始化为非空值的将被关闭。

    2) 如果在初始化资源时抛出异常(如 例如),这将属于不成功的初始化场景。 这种情况下,我们怎么会遇到抛出异常的场景呢? 在 try-with-resources 语句中(基本上只能发生 在初始化期间)在所有?是不是抛出的异常 关闭资源?

    try-with-resources 语句是一个复合语句,包含从最初的 try 关键字到相关块的所有内容,包括任何 catchfinally 子句。主要关注点是在try 块内部抛出异常的情况下正确清理。规范中关于资源初始化期间发生异常的情况的位可以理解为 N-resource try-with-resources 语句和 N 嵌套语句之间的等效性单一资源 try-with-resources 语句。

    还要注意,JLS 使用传统的 try / catch / finally 和显式资源关闭将 try-with-resources 语句的 a translation 呈现为等效代码。如果您正在为 try-with-resources 的语义而苦苦挣扎,那将是一个很好的考虑资源。

    【讨论】:

    • 感谢约翰的回复。它有助于。还有 3 个后续问题需要澄清:1)当资源自动关闭时,JLS-catch 子句和 finally 子句的摘录通常是不必要的。但在我的示例中,读取字符流时可能会出现 IOException。在这种情况下,如果我们不处理它 - 代码不会被编译。因此,我们必须用各自的 catch 块包围它。因此,JLS 中的通用语句说 catch 和 finally 子句通常是不必要的(至少是 catch 子句)让我有点困惑。我弄错了吗?
    • 2) “try-with-resources 语句是复合语句..”。因此,在我的示例中,try with resources 语句包括括号中的资源初始化、关联块和以下 catch 子句。正确的? [PS:即使在官方 Java 页面中,我们也将 try-with-reources 语句和 try 块视为单独的术语。这让我有点困惑。]
    • 3) 在我引用的示例中,资源未成功初始化,因为资源不会自动关闭 - 如果有一个 catch 块来处理它(我在我的示例中有) ,它将作为常规的 try-catch 处理,否则,程序终止。对吗?
    • @sake,(1) 你不需要 try-with-resources 上的 catch 块。您可以选择声明该方法抛出异常,或者在方法内部但在 try-with-resources 之外捕获异常。此外,一些资源类型可能会实现一个close(),它不声明任何检查异常。
    • @sake, (2) 是的,在您的示例中,try-with-resources 以 try 关键字开头,并包含从那里到 catch 块末尾的所有内容。从the BNF representation in the JLS 可以看出这一点。我不确定您正在查看哪些其他文档让您感到困惑,但 JLS 在这些问题上是权威的。
    【解决方案2】:

    1) 是的。如果资源初始化成功并且try块有异常,资源将被关闭,就像我们在finally块中一样,不使用资源进行尝试。

    2) 文档说(稍作修改):

    如果不使用资源尝试,如果在一个方法中,read()和close()(在finally块中)都抛出异常,则该方法抛出finally块抛出的异常;从 try 块抛出的异常被抑制。相反,使用 try with resource 时,如果 try 块和 try-with-resources 语句都抛出异常,则该方法会抛出 try 块抛出的异常;从 try-with-resources 块抛出的异常被抑制。

    正如您所提到的,第二点中 try-with-resource 的异常将来自关闭资源。

    【讨论】:

      【解决方案3】:

      try-with-resources 是旧语法的语法糖,使用旧语法编写它可以帮助您理解它的作用:

      这样的例子:

        try (AutoCloseable ac1 = ac1(); AutoCloseable ac2 = ac2()) {
          ac2.doWhatever();
        } catch (Exception e) { 
          fail(e);
        }
      

      改写成这样:

        AutoCloseable ac1 = null;
        try {
          ac1 = ac1();
          AutoCloseable ac2 = null;
          try {
            ac2 = ac2();
            ac2.doWhatever();
          } catch (Exception e) { 
            fail(e);
          } finally {
            if (ac2 != null) ac2.close();
          }
        } catch (Exception e) { 
          fail(e);
        } finally {
          if (ac1 != null) ac1.close();
        }
      

      编译器可能会优化代码:

      • finally 块只能有一个,它可能会将两者合并。
      • 使用一些合成函数来处理相同的捕获。

      如果您有兴趣,这个presentation by E. Mandrikov 解释了编译器的作用以及代码覆盖率如何令人头疼。

      如你所见,它可能在初始化部分失败:

      • ac1 初始化可能会失败,在这种情况下,变量将是 null(默认值)。
      • ac2 初始化可能失败,ac1 不会为空。
      • ac1ac2 可能会起作用,在这种情况下,只有 doWhatever() 会导致失败。

      如果ac2依赖于ac1,并且不在资源列表中,例如:

      try (AutoCloseable ac2 = ac2(ac1())) {
        ...
      }
      

      然后,ac1() 构建的 AutoCloseable 不会关闭,除非它在 ​​ac2::close 中关闭。并且垃圾收集器不会调用ac1::close

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-18
        相关资源
        最近更新 更多