我刚刚在我们的一个Java库中遇到了一个隐藏的gem:
for(Widget w : widgets) {
if(shouldDoStuff()) {
try{
// Do stuff to w.
} catch(Exception e){
throw new RuntimeException("Couldn't do stuff.");
} finally{
// Compiler warning: finally block does not complete normally
continue;
}
}
}
我知道finally
胜过一切,但我想知道两件事:
catch
子句 执行时会发生什么?异常是否被抛出?首先发生什么:抛出的异常或continue
语句?我找到this very similar question但是接受的答案只是声明异常会突然突然,我不确定这意味着什么。此外,它并没有真正帮助我理解我上面提到的关于将要发生的事件顺序的第一个问题。
答案 0 :(得分:5)
“finally”将在抛出RuntimeException之后执行,并在由堆栈中的另一个catch upper处理之前执行。
正如你最后只是继续,实际上它什么都不做。
矛盾是在结束循环的捕获和继续之间。
一种方法可能是:
boolean exceptionOccured = false;
for(Widget w : widgets) {
if(shouldDoStuff()) {
try {
// Do stuff to w.
} catch(Exception e){
exceptionOccured = true; // do not throw yet.
e.printStackTrace();
}
}
}
if (exceptionOccured) {
throw new RuntimeException("Couldn't do stuff.");
}
这种方法的主要问题是你不知道出了什么问题。
答案 1 :(得分:1)
抛出异常。如果跑了,你会看到:
RuntimeException("Couldn't do stuff.")
finally
。如果有一个外部try / catch抓住了RuntimeException
,那么程序可以继续。 continue
块中的finally
语句无用。如果你调用System.exit()或者JVM首先崩溃,那么最后不会被调用的唯一时间。
答案 2 :(得分:0)
表格代码
try {
// non-control-flow statements A
} finally {
// non-control-flow statements B
}
编译就好像你写了:
try {
// non-control-flow statements A
} catch(Throwable t) {
// non-control-flow statements B
throw t;
}
// non-control-flow statements B
这样就可以在任何一种情况下执行语句B,例外或非例外。但是如果你在finally块中插入一个像continue
这样的控制流语句,你就会把隐藏的重新throw t;
语句变成死代码。
有一种简单的方法可以在没有警告的情况下实现相同目的。编写一个显式异常处理程序,执行相同但不包含无效的重新throw
语句。这样你就可以记录吞下异常是故意的。然后警告就会消失。然而,吞咽异常仍然是糟糕的代码风格。