Java的try-with-resources是否会捕获错误或只是异常?

Ali*_*aka 53 java junit exception-handling exception

我有一些junit测试可以创建一些应该关闭的资源.

实现此逻辑的一种方法是使用@Before@After方法.

我所做的是将创建封装在一些实用程序类中以供重用.例如:

class UserCreatorTestUtil implements AutoClosable {
  User create() {...}
  void close() {...}
}
Run Code Online (Sandbox Code Playgroud)

关键是对象要关闭自己,而不是需要记住关闭它@After.

用法应该是:

@Test
void test() {
  try (UserCreatorTestUtil userCreatorTestUtil = new UserCreatorTestUtil()) {
    User user = userCreatorTestUtil.create();
    // Do some stuff regarding the user's phone
    Assert.assertEquals("123456789", user.getPhone());
  }
}
Run Code Online (Sandbox Code Playgroud)

问题是junit的assert关键字抛出Error- 而不是Exception.

try-with-resource会"捕获" Error并调用close方法吗?

*在try-with-resources文档中找不到答案.

Thi*_*ilo 73

catch什么都没有.但它确实finally关闭了所有资源.

finally即使抛出错误,也会运行块.

  • 它的行为*几乎就像一个`finally`块,因为,与`finally`块不同,它将捕获所有后续`Throwable'的`close()`操作并将它们添加为*suppress*throwable到主throwable .一个`finally`块不能这样做,因为它没有关于主要throwable的知识,如果有的话. (12认同)
  • 请注意,除了`System.exit()`之外,最终效果很好:http://stackoverflow.com/questions/14905006/system-exit-results-unexecutable-finally-block (5认同)
  • 是的,它以与手动`finally`块相同的方式进行清理. (2认同)
  • @Holger:嗯,它实现为`finally`块*另外*一个`catch`块,它不会真正捕获任何东西(只记录主要的throwable和rethrows).所有"动作"都发生在finally块中.有关详细信息,请参阅@Nicolas答案. (2认同)

Nic*_*tto 37

基本的try-with-resources语句的伪代码是(cf Java Language Specification §14.20.3.1):

final VariableModifiers_minus_final R Identifier = Expression;
Throwable #primaryExc = null;

try ResourceSpecification_tail
    Block
catch (Throwable #t) {
    #primaryExc = #t;
    throw #t;
} finally {
    if (Identifier != null) {
        if (#primaryExc != null) {
            try {
                Identifier.close();
            } catch (Throwable #suppressedExc) {
                #primaryExc.addSuppressed(#suppressedExc);
            }
        } else {
            Identifier.close();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样,它捕获的Throwable不是Exception包含Error只是为了获取主要异常,以便将关闭资源时发生的任何异常添加为抑制异常.

您还可以注意到您的资源在finally块中被关闭,这意味着它们将被关闭,无论发生什么情况(System.exit当然,当它终止当前运行的Java虚拟机时),即使在抛出一个Error或任何子类的情况下Throwable也是如此.


And*_*ner 12

尝试使用资源不会捕获任何内容.

但是,您可以将catch块附加到try-with-resources块的末尾,以捕获Throwable您喜欢的任何类型:

try (UserCreatorTestUtil userCreatorTestUtil = new UserCreatorTestUtil()) {
  // ... Whatever
} catch (RuntimeException e) {
  // Handle e.
} catch (Exception | Throwable t) {
  // Handle t.
}
Run Code Online (Sandbox Code Playgroud)


Ram*_*asi 8

背后的想法try-with-resources是确保资源应该关闭.

常规try-catch-finally语句的问题是让我们假设你的try块抛出一个异常; 现在通常你会在finally块中处理该异常.

现在假设finally块中也发生异常.在这种情况下,try catch抛出的异常将丢失,并且finally块中生成的异常会被传播.

try {
    // use something that's using resource
    // e.g., streams
} catch(IOException e) {
   // handle 
} finally {
    stream.close();
    //if any exception occurs in the above line, than that exception
    //will be propagated and the original exception that occurred
    //in try block is lost.
}
Run Code Online (Sandbox Code Playgroud)

在资源try-with-resourcesclose()方法中将自动调用,如果close()抛出任何异常,finally则不会到达其余部分,并且原始异常将丢失.

与此形成鲜明对比:

try (InputStream inputStream= new FileInputStream("C://test.txt")){
    // ... use stream
} catch(IOException e) {
   // handle exception
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中,该close()方法会自动被调用,如果该close()方法也生成了任何异常,则该异常将自动被抑制.

另请参见:Java语言规范14.20.3