【问题标题】:Is the close method on a try-with-resources idiom not called if a constructor throws an exception?如果构造函数抛出异常,是否不调用 try-with-resources 习语上的 close 方法?
【发布时间】:2016-04-13 05:22:32
【问题描述】:

我有一个基类Base 和一个子类Child 扩展它。 Base 实现 java.lang.AutoCloseable

假设Child 的构造函数抛出Foo

现在考虑

try (Base c = new Child()){
    /*Some code*/
} catch (final Foo e){
    /*Some more code*/
}

如果抛出异常,是否调用Base#close 方法?它不在我的机器上,但这是 JLS 标准化的东西吗?

【问题讨论】:

  • 如果“Child的构造函数抛出Foo”,则调用范围内不存在Child的实例,因此无法调用close()
  • 如果没有对象,就无法调用close

标签: java try-with-resources


【解决方案1】:

是的,close 不会被调用。这是在 JLS section 14.20.3 中指定的:

资源按从左到右的顺序初始化。 如果资源初始化失败(即其初始化表达式抛出异常),那么到目前为止由 try-with-resources 语句初始化的所有资源都将关闭。如果所有资源初始化成功,try 块将正常执行,然后 try-with-resources 语句的所有非空资源都将关闭。

资源的关闭顺序与它们初始化时的顺序相反。 资源只有在初始化为非空值时才会关闭。关闭一个资源的异常不会阻止关闭其他资源。如果先前由初始化程序、try 块或资源关闭引发了异常,则此类异常将被抑制。

在这种情况下,构造函数中会引发异常,因此资源不会被初始化为非空值。因此,close 方法不会被调用。

【讨论】:

    【解决方案2】:

    close 不会被调用。调用它是没有意义的,因为您没有要关闭的完全构造的对象,并且在类似的调用中,您甚至可能没有输入构造函数:

    try (Base b = makeBase()) {
        ...
    }
    

    makeBase 在哪里

    Base makeBase() {
        throw new RuntimeException();
    }
    

    【讨论】:

    • 但是基类完全构造的。这就是我正在与之斗争的。
    • @P45Imminent:Java 没有任何基本子对象的概念。没有完全构建的“基础”对象;在 Child 构造函数完成之前,该对象还没有完全构造。
    • 谢谢。投了赞成票,但遗憾的是不能接受两个答案。
    【解决方案3】:

    这可能不是处理它的最佳方法,但也许您可以延迟抛出异常,直到对象被构造之后。如何?您可以在类上有一个异常属性,并向该类的公共方法添加一个子句,如果该异常属性不为空,则可以抛出在对象实例化期间产生的缓存异常。这样,您应该能够回收在相关对象的部分构造期间可能已分配的资源。我相信这就是这些 IO 读取器和写入器类处理这些情况的方式。

    同样,这可能不是最好的方法,但如果你真的需要,它可能是需要考虑的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-23
      • 2018-06-15
      • 1970-01-01
      • 1970-01-01
      • 2014-08-02
      • 1970-01-01
      相关资源
      最近更新 更多