Java';尝试使用资源捕获错误还是仅捕获异常?
我有一些junit测试,它们创建了一些应该关闭的资源 实现此逻辑的一种方法是使用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
@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
notException
,其中包括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
}