【问题标题】:amazing output for try/catch/finally? [duplicate]try/catch/finally 的惊人输出? [复制]
【发布时间】:2014-03-05 13:09:25
【问题描述】:

我运行这段代码:

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 是个坏习惯吗?

【问题讨论】:

  • finally 将被总是调用(如果没有崩溃)。
  • 在我点击你的问题之前,只看到标题,我就知道我会在你的finally 块中找到return。纳夫说。
  • finally 块总是被执行,不管 try 或 catch 块中发生了什么。
  • @MarkoTopolnik 这里也一样 :)

标签: java try-catch


【解决方案1】:

return 2 语句去哪里了?

没了。

具体what the JLS says is:

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

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

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

return 语句是abrupt completion 的原因。)

return at finally 是个坏习惯吗?

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

例如观察以下内容:

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

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

【讨论】:

    【解决方案2】:

    这并不奇怪。

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

    如果你在 finally 中没有返回任何东西,那么将要成为返回值的前一个值就是返回值。

    您可以从字节码中看到,如果您在 finally 块中返回,则返回值将被更新。

    附注:

    the finally block 用于特殊用途。

    finally 不仅仅用于异常处理——它允许程序员避免清理代码被 return、continue 或 break 意外绕过。将清理代码放在 finally 块中始终是一种很好的做法,即使没有预料到异常也是如此。

    不推荐在里面写你的逻辑。

    【讨论】:

      【解决方案3】:

      在 JSE7 语言规范§14.1 中,return 语句被定义为突然终止。如果你的finally 块突然完成(你的returntry 块以同样的原因结束(定义在§14.20.2):

      §14.1 [...] 突然完成总是有一个相关的原因,它是以下之一: [...] 没有值的返回 [...] 有给定值的返回 [...]

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

      【讨论】:

        【解决方案4】:

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

        “finally 块总是在 try 块退出时执行。这确保了 finally 块即使发生意外异常也会被执行。但 finally 不仅仅用于异常处理——它允许程序员 避免清理代码意外地被 return、continue 或 break 绕过。将清理代码放在 finally 块中始终是一个好习惯,即使没有预料到异常也是如此。”

        【讨论】:

          【解决方案5】:

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

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

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

          【讨论】:

            【解决方案6】:

            该行为的原因是finally总是 执行,因此当@ 中有return 时,您不能在catch 块上结束方法执行987654324@块。

            那么一步一步实际发生了什么:

            1. TRY 去输出
            2. thrower()方法被执行
            3. 它抛出new RuntimeException()
            4. catch 块中的 catcher() 捕获它
            5. CATCH 去输出
            6. finally 块被执行
            7. FINALLY 去输出
            8. 并且该方法返回 3 - 你不能回到 catch

            【讨论】:

              【解决方案7】:

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

              而且你期望“finally”被打印出来。 finally 旁边的 return 语句是 3。那么您如何期望返回 2。

              【讨论】:

                【解决方案8】:

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

                【讨论】:

                  【解决方案9】:

                  编译器在返回之前将“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
                      }
                  }
                  

                  这就是你看到的原因

                  试试 抓住 最后 3

                  return at finally 是一种不好的做法吗?

                  是的,理论上。如果你了解它是如何工作的,你可以在“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;
                      }
                  }
                  

                  返回

                  试试 抓住 最后 2

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

                  【讨论】:

                    猜你喜欢
                    • 2013-11-09
                    • 2014-11-27
                    • 1970-01-01
                    • 2013-02-19
                    • 2016-03-28
                    • 2011-02-20
                    • 2019-02-28
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多