try / catch / finally的惊人输出?

时间:2014-03-05 13:09:26

标签: java try-catch

我运行此代码:

public static void main(String[] args) {
    System.out.println(catcher());
}

private static int catcher() {
    try {
        System.out.println("TRY");
        thrower();
        return 1;
    } catch (Exception e) {
        System.out.println("CATCH");
        return 2;
    } finally {
        System.out.println("FINALLY");
        return 3;
    }
}

private static void thrower() {
    throw new RuntimeException();
}

我希望在输出时看到这个:

TRY
CATCH
FINALLY
2

但令人惊讶的是输出是:

TRY
CATCH
FINALLY
3

我很困惑。 return 2陈述在哪里? return at finally是一种不良做法吗?

9 个答案:

答案 0 :(得分:4)

  

哪里返回2语句?

它消失了。

具体来说what the JLS says is

  

如果catch块由于原因R突然完成,则执行finally块。然后有一个选择:

     
      
  • 如果finally块正常完成,则try语句突然完成,原因为R。

  •   
  • 如果finally块由于原因S突然完成,则try语句突然完成,原因为S(并且原因R被丢弃)。

  •   

return语句是abrupt completion的原因。)

  

最终回归是一种糟糕的做法吗?

基本上是的。因为没有充分的理由这样做,它会吃掉异常。

例如,请注意以下内容:

static void eatAnException() {
    try {
        throw new RuntimeException("oops");
    } finally {
        return;
    }
}

最后的return语句丢弃了异常。相反,在finally块之后放置一个return语句。

答案 1 :(得分:2)

这并不奇怪。

这是实际行为。返回值在finally块确定。

如果你最后没有返回任何内容,那么前一个值将成为返回值是返回值。

你可以从字节代​​码看到,如果你要返回,那么返回值将在finally块中更新。

作为旁注:

the finally block是出于特殊目的。

  

最终不仅仅用于异常处理 - 它允许程序员避免因返回,继续或中断而意外绕过清理代码。将清理代码放在finally块中总是一种很好的做法,即使没有预期的例外情况也是如此。

不建议在其中编写逻辑。

答案 2 :(得分:1)

Oracle文档(http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html)声明:

“finally块总是在try块退出时执行。这确保即使发生意外异常也会执行finally块。但最终不仅仅是异常处理有用 - 它允许程序员避免通过返回,继续或中断意外绕过清理代码。将清理代码放在finally块中总是一个好习惯,即使没有预期的例外情况。“

答案 3 :(得分:1)

finally块中返回的任何内容实际上都会覆盖try / catch块中的任何异常或返回值

在几个独特的场景中返回后,不会调用finally块:如果首先调用System.exit(),或者JVM崩溃。

这就是为什么在finally块中包含return语句被认为是一个非常糟糕的主意。

答案 4 :(得分:1)

在JSE7语言规范§14.1 中,return语句被定义为突然终止。如果您的finally数据块突然完成(您的return),则try数据块会因同样的原因(如§14.20.2中所定义)结束:

<强>§14.1 [...]突然完成总是有一个相关的原因,这是以下之一:[...]没有价值的回报[...]具有给定价值的回报[...]

<强>§14.20.2 [...]如果finally块因原因S而突然完成,则try语句突然完成,原因为S(并且原因R被丢弃)。 [...] (原因R是catch)的结果。

答案 5 :(得分:0)

该行为的原因是finally始终已执行,因此当catchreturn时,您无法结束方法执行1 {}在finally块中。

所以实际上一步一步地发生了什么:

  1. TRY转到输出
  2. 执行
  3. thrower()方法
  4. 抛出new RuntimeException()
  5. catch阻止catcher()抓住它
  6. CATCH转到输出
  7. 执行
  8. finally
  9. FINALLY转到输出
  10. 该方法返回3 - 您无法返回catch

答案 6 :(得分:0)

编译器在返回之前将“finally”部分(通常是子程序的调用)插入到所有分支中(“当try块退出时”)。这意味着你的方法正在改变如下:

private static int catcher() {
    try {
        System.out.println("TRY");
        thrower();

        // -- finally section --
        System.out.println("FINALLY");
        return 3;
        // ---------------------

        return 1; // will be eliminated by the compiler
    } catch (Exception e) {
        System.out.println("CATCH");

        // -- finally section --
        System.out.println("FINALLY");
        return 3;
        // ---------------------

        return 2; // will be eliminated by the compiler
    }
}

这就是你看

的原因

TRY 抓住 最后 3

  

最终回归是一种糟糕的做法吗?

是的,从理论上讲。如果你理解它是如何工作的,你可以在“finally”中使用“return”:)但最好只使用“finally”来释放资源。

还有一个有趣的案例:

private static int catcher2() {
    int v = 1;
    try {
        System.out.println("TRY");
        thrower();
        return v;
    } catch (Exception e) {
        System.out.println("CATCH");
        v = 2;
        return v;
    } finally {
        System.out.println("FINALLY");
        v = 3;
    }
}

返回

TRY 抓住 最后 2

因为在字节码中返回之前的最后一个操作(ireturn /“当try块退出时”)是iload(加载一个保存在堆栈中的值,此刻它有“2”)

答案 7 :(得分:0)

无论是否抛出异常,都会调用finally块。在catch块之后,流程返回2以最终阻塞。 finally块再次返回3到主程序。

更多的是你希望“终于”打印出来。紧接着最后的return语句是3.那么你如何期望2被返回。

答案 8 :(得分:0)

如果finally块执行控制转移语句,例如返回或带标记的中断,那么finally块中的return语句返回的值将取代try块或catch中的return语句返回的任何值块。