【问题标题】:Java: how does finally trump throw?Java:最终王牌如何投掷?
【发布时间】:2014-02-28 14:11:24
【问题描述】:

我刚刚在我们的一个 Java 库中遇到了一个隐藏的宝石:

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 胜过一切,但我想知道两件事:

  1. catch 子句执行 时会发生什么?异常是否被抛出?首先会发生什么:抛出的异常还是 continue 语句?
  2. 如何重写此代码以消除警告?

我找到了this very similar question,但接受的答案只是指出突然会抛出异常,我不确定这意味着什么。另外,它并不能真正帮助我理解我上面关于将要发生的事件顺序的第一个问题。

【问题讨论】:

  • “胜过一切”是什么意思?
  • 这是stackoverflow.com/questions/5126455/… 的副本,它确实很好地回答了这个问题(阅读解释)。基本上例外是被回报吞没。基本上使投掷无用。
  • 最后块应该做清理任务之类的结束。它不应该做除此之外的其他事情。如果您删除 continue 语句,它将不会显示警告。
  • 2.如果continue;finally 块中的唯一语句,只需将其移出try/catch 并删除finally
  • 这是另一个类似的 SO 问题,有很多解释:stackoverflow.com/questions/65035/…

标签: java exception compiler-warnings throw try-catch-finally


【解决方案1】:

"finally" 将在你的 RuntimeException 被抛出之后,在堆栈中的另一个 catch 上层处理之前执行。

当您的 finally 继续时,实际上它什么也不做。

矛盾在于将结束循环的 catch 中的 throw 和 continue 之间。

一种方法可能是:

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.");
}

这种方法的主要问题是您不知道出了什么问题。

【讨论】:

  • continue 语句不属于finally 块,这就是编译器抱怨的原因。 (return 也不属于那里。)除此之外,这是正确的答案; finally 在块退出之前运行,无论它如何退出。不,我也不知道abruptly 是什么意思。
  • 感谢@Nicolas Defranoux (+1) - 请在 GI Joe 的回答下方查看我的评论 - 我有同样的问题要问你!
  • continue 试图继续循环,但是由于 catch 中有一个 throw,所以 finally 被执行,然后异常被抛出更高,并且循环无论如何都被打破了。
  • 问题是:你想做什么?如果您想处理列表中的所有元素,即使发生一些异常,也请从 catch 中删除 throw。您仍然可以存储异常并在循环结束后抛出异常,但如果有多个异常您会怎么做?
  • 为什么不记得实际的异常而不是boolean?然后你可以重新抛出真正的异常而不是一个不特定的RuntimeException
【解决方案2】:

抛出异常。如果运行,您会看到:

  1. RuntimeException("Couldn't do stuff.")
  2. 执行finally 中的内容。

如果有一个外部 try/catch 捕获了 RuntimeException,那么程序可以继续执行。 finally 块中的 continue 语句没有用。唯一不会调用 finally 的情况是调用 System.exit() 或 JVM 先崩溃。

【讨论】:

  • 谢谢@GI Joe (+1) - 你能解释一下为什么continue 声明无用吗?我认为这段代码的作者本质上是想分叉执行并重新抛出异常,但仍要继续处理Widgets 的列表。
  • @TotesMcGotes:这个例子展示了一个异常反模式。这是一个不好的例子。此外,JDK7+ 中不需要使用finally。但它通常用于清理资源。
【解决方案3】:

表格代码

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 在任何一种情况下都会执行,无论是异常的还是非异常的。但是,如果您将continue 之类的控制流语句插入到 finally 块中,您会将隐藏的 re-throw t; 语句变成死代码。

有一种简单的方法可以在没有警告的情况下实现同样的效果。编写一个显式异常处理程序,执行相同但不包含无效的 re-throw 语句。这样您就可以记录吞下异常是故意的。然后警告将消失。但是,吞下异常仍然是不好的代码风格。

【讨论】:

    猜你喜欢
    • 2014-04-27
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 2021-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多