如果构造函数抛出异常,是否不调用 try-with-resources 习惯用法的 close 方法?

Is the close method on a try-with-resources idiom not called if a constructor throws an exception?

我有一个基础 class Base 和一个扩展它的子 class ChildBase 实施 java.lang.AutoCloseable.

假设 Child 的构造函数抛出 Foo.

现在考虑

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

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

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

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

其中 makeBase

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

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

中指定的

Resources are initialized in left-to-right order. If a resource fails to initialize (that is, its initializer expression throws an exception), then all resources initialized so far by the try-with-resources statement are closed. If all resources initialize successfully, the try block executes as normal and then all non-null resources of the try-with-resources statement are closed.

Resources are closed in the reverse order from that in which they were initialized. A resource is closed only if it initialized to a non-null value. An exception from the closing of one resource does not prevent the closing of other resources. Such an exception is suppressed if an exception was thrown previously by an initializer, the try block, or the closing of a resource.

在这种情况下,构造函数中抛出异常,因此资源未初始化为 non-null 值。因此,不会调用 close 方法。

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

同样,这可能不是最好的方法,但如果您真的需要的话,这可能是一个值得考虑的方法。