Java';尝试使用资源捕获错误还是仅捕获异常?

Java';尝试使用资源捕获错误还是仅捕获异常?,java,exception,junit,exception-handling,Java,Exception,Junit,Exception Handling,我有一些junit测试,它们创建了一些应该关闭的资源 实现此逻辑的一种方法是使用@Before和@After方法 我所做的是将创建封装在某个实用程序类中以供重用。例如: class UserCreatorTestUtil implements AutoClosable { User create() {...} void close() {...} } 整个要点是让对象自行关闭,而不需要记住在之后的@中关闭它 用法应为: @Test void test() { try (UserC

我有一些junit测试,它们创建了一些应该关闭的资源

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

我所做的是将创建封装在某个实用程序类中以供重用。例如:

class UserCreatorTestUtil implements AutoClosable {
  User create() {...}
  void close() {...}
}
整个要点是让对象自行关闭,而不需要记住在之后的
@中关闭它

用法应为:

@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());
  }
}
问题是junit的assert关键字抛出了一个
错误
——而不是
异常

try with resource会“捕获”
错误并调用close方法吗?


*在中找不到答案。

它没有捕获任何内容。但它确实最终关闭了所有资源


最后
阻塞。

你这方面的误解:使用资源进行尝试不会造成问题

它会做一个finalfinally,因此“问题”并不重要


有关更多信息,请参阅

试着利用资源,不要抓住资源本身的任何东西

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

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

基本try with resources语句的伪代码为(cf):

如您所见,它捕获了
Throwable
not
Exception
,其中包括
Error
,但仅获取主异常,以便将关闭资源时发生的任何异常添加为抑制异常


您还可以注意到,您的资源在
finally
块中被关闭,这意味着无论发生什么情况,它们都将被关闭(系统
的情况除外。当然退出
,因为它会终止当前运行的Java虚拟机)即使在抛出
错误
或任何
可丢弃的子类
的情况下。

尝试使用资源
背后的想法是确保关闭资源

传统的
try-catch-finally
语句的问题是,假设您的
try
块抛出异常;现在,通常您将在
finally
块中处理该异常

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

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.
}
try with resources
中,将自动调用资源的
close()
方法,如果
close()
抛出任何异常,则最后将无法到达
的其余部分,原始异常将丢失

与此形成对比:

try (InputStream inputStream= new FileInputStream("C://test.txt")){
    // ... use stream
} catch(IOException e) {
   // handle exception
}
在上面的代码段中,将自动调用
close()
方法,如果该
close()
方法也生成了任何异常,那么该异常将自动被抑制


另请参见:

OT:只是为了记录:“我有一些junit测试可以创建一些资源”,所以您没有UnitTests,您有的是使用UnitTest-Framework的模块测试。@TimothyTruckle-在挑刺时:一些资源也可以引用模拟版本,为了让一切都有意义,这仍然必须关闭。错误和异常(大写E)都是异常(小写)。@immibis,但我们可以称它们为不区分大小写且无歧义的Throwables。@Thilo但不区分大小写的语言实际上是撒旦。不,它根本不知道任何异常/错误。是的,它以与手动
finally
块相同的方式进行清理。请注意,finally除了
System.exit()
:它的行为几乎像
finally
块,因为与
finally
块不同,它将捕获
close()的所有后续
可丢弃的
s
操作,并将其作为抑制的可丢弃项添加到主可丢弃项。
finally
块不能做到这一点,因为它不知道主可丢弃对象(如果有的话)。@Holger:好吧,它是作为一个
finally
块来实现的,除了一个
catch
块之外,这个块实际上没有捕获任何对象(只记录主可丢弃对象和重抛出对象)。所有的“动作”都发生在finally块中。有关详细信息,请参见@Nicolas answer。不幸的是,
javac
严格遵守了这个复杂的正式规范。考虑到这两种情况下,
finally
最终是通过代码复制实现的,因此将异常存储在变量中并针对
null
进行测试毫无意义,当代码路径已经暗示是否存在异常时…@Holger:代码路径并不暗示是否存在异常。请记住,无论是否存在异常,都将执行
finally
块。你说的代码重复是什么意思(我猜是两个
Identifier.close()
),为什么它会让你烦恼?您建议尝试使用资源应该做什么呢?@siegi:在字节码级别上,没有
finally
功能,因此,通过将操作复制到可能离开块的每个代码路径,再加上一个异常处理程序,即
try{foo();}finally{bar();},可以编译
finally
编译为
try{foo();}catch(Throwable t){bar();throw t;}bar(),复制调用代码。在这里,代码路径暗示是否发生异常,即第一个
bar()调用是在异常情况下的调用,第二个是没有调用的调用。@siegi:javac产生的代码在中讨论。在我的回答中,我是
try (InputStream inputStream= new FileInputStream("C://test.txt")){
    // ... use stream
} catch(IOException e) {
   // handle exception
}