题 最终总是在Java中执行吗?


考虑到这段代码,我可以吗? 绝对肯定 那个 finally 无论如何,块总是执行 something() 是什么?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("i don't know if this will get printed out.");
}

1907
2017-09-15 17:43


起源


即使在自己测试之后,你也只知道什么 你的 Java / JVM的实现或版本都会做,而且不知道代码是什么 应该 做(根据标准),所以问题仍然是有效的。我很高兴看到这些方面在各种答案中得到了解决。 - Rhubbarb
不 总是 - Boann
有效的java说不然 informit.com/articles/article.aspx?p=1216151&seqNum=7 - Binoy Babu
@BinoyBabu, 终结 != finally; 终结 == finalize() 方法。 - jaco0646
@LordFarquaad ;-)确实总能得到保证。 - MC Emperor


答案:


是, finally 将在执行try或catch代码块后调用。

唯一的一次 finally 将不会被称为:

  1. 如果你调用 System.exit();
  2. 如果JVM首先崩溃;
  3. 如果JVM在。中达到无限循环(或其他一些不可中断的,非终止语句) try 要么 catch 块;
  4. 如果操作系统强行终止JVM进程;例如UNIX上的“kill -9”。
  5. 如果主机系统死亡;例如电源故障,硬件错误,操作系统恐慌等。
  6. 如果最终块将由守护程序线程执行,并且所有其他非守护程序线程在最终被调用之前退出。

2122
2017-09-15 17:45



其实 thread.stop() 不一定能阻止 finally 阻止被执行。 - Piotr Findeisen
我们怎么说呢 finally 块将被调用 后 该 try 块,和 之前 控制传递给以下语句。这与涉及无限循环的try块一致,因此finally块实际上从未被调用过。 - Andrzej Doyle
当我们使用嵌套时,还有另一种情况 的try-catch-终于 块 - ruhungry
另外,在守护进程线程抛出异常的情况下,最后不会调用block。 - Amrish Pandey
@BinoyBabu - 这是关于终结者,而不是最终阻止 - avmohan


示例代码:

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

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

输出:

finally trumps return. 
0

446
2017-09-15 17:59



仅供参考:在C#中,除了替换语句中的语句之外,行为是完全相同的 finally - 使用 return 2; 不允许(编译器错误)。 - Alexander Pacha
这是一个需要注意的重要细节: stackoverflow.com/a/20363941/2684342 - WoodenKitty
您甚至可以在finally块本身中添加一个return语句,然后它将覆盖以前的返回值。这也神奇地丢弃了未处理的异常。此时,您应该考虑重构代码。 - Zyl
这并没有真正证明最终胜过归来。从调用者代码打印返回值。似乎没有多少证据。 - Trimtab
对不起,这是演示而不是证明。如果您能够证明此示例在所有Java平台上始终以这种方式运行,那么这只是一个证明,并且类似示例也始终以这种方式运行。 - Stephen C


此外,虽然这是不好的做法,如果在finally块中有一个return语句,它将胜过常规块中的任何其他返回。也就是说,以下块将返回false:

try { return true; } finally { return false; }

从finally块中抛出异常也是一样的。


325
2017-09-15 18:19



这是一个非常糟糕的做法。看到 stackoverflow.com/questions/48088/... 了解更多关于它为什么不好的信息。 - John Meagher
同意。 finally {}内的返回忽略try {}中抛出的任何异常。害怕! - neu242
@ dominicbri7为什么你认为这是一个更好的做法?当函数/方法无效时,为什么它会有所不同? - corsiKa
出于同样的原因,我从不在我的C ++代码中使用goto。我认为多次返回会使其难以阅读并且更难以调试(当然,在非常简单的情况下,它不适用)。我想这只是个人的偏好,最后你可以用任何一种方法达到同样的目的 - dominicbri7
当某种特殊情况发生时,我倾向于使用一些回报。如果(有理由不继续)返回; - iHearGeoff


这是Java语言规范中的官方文字。

14.20.2。执行try-finally和try-catch-finally

一个 try 声明与 finally 块首先执行 try 块。然后有一个选择:

  • 如果执行了 try 块正常完成,[...]
  • 如果执行了 try 由于a,块突然完成 throw 一个价值 V,[...]
  • 如果执行了 try 由于任何其他原因,块突然完成 [R那么 finally 块被执行。然后有一个选择:
    • 如果finally块正常完成,那么 try 声明突然完成了 [R
    • 如果 finally 阻止突然完成的原因 小号那么 try 声明突然完成了 小号 (和原因 [R 被丢弃了)。

规范 return 实际上这是明确的:

JLS 14.17返回声明

ReturnStatement:
     return Expression(opt) ;

一个 return 声明没有 Expression  尝试 将控制转移到包含它的方法或构造函数的调用者。

一个 return 声明与 Expression  尝试 将控制权转移给包含它的方法的调用者;的价值 Expression 成为方法调用的值。

前面的描述说“尝试 转移控制“而不仅仅是”转移控制“因为如果有的话 try 方法或构造函数中的语句 try 块包含 return 声明,然后任何 finally 那些条款 try 在将控制权转移给方法或构造函数的调用者之前,将按顺序执行语句,从最里面到最外层。突然完成一个 finally 条款可以破坏由a发起的控制权转移 return 声明。


229
2018-05-25 06:50





除了其他响应之外,重要的是要指出'finally'有权通过try..catch块覆盖任何异常/返回值。例如,以下代码返回12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

同样,以下方法不会抛出异常:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

虽然以下方法确实抛出它:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

134
2018-05-13 07:11



应该注意的是,中间的情况正是为什么在finally块中包含return语句是绝对可怕的(它可以隐藏任何Throwable)。 - Dimitris Andreou


我尝试了上面的例子,略有修改 -

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

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

上面的代码输出:

最后胜过回归。
  2

这是因为什么时候 return i; 被执行 i 有一个值2.在此之后 finally 执行块,其中12被分配给 i 接着 System.out out已执行。

执行后 finally 阻止 try block返回2,而不是返回12,因为这个return语句不会再次执行。

如果您将在Eclipse中调试此代码,那么您将在执行后感觉到 System.out 的 finally 阻止 return 声明 try 块再次执行。但这种情况并非如此。它只返回值2。


88
2017-11-17 16:25



这个例子很棒,它增加了几十个最终相关线程中没有提到的东西。我认为几乎没有任何开发人员会知道这一点。 - HopefullyHelpful
如果 i 不是原始的,而是Integer对象。 - Yamcha
我很难理解这个案子。 docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 说,“带有Expression的返回语句试图将控制转移到方法的调用者或包含它的lambda体......如果表达式的评估正常完成,则生成值V ..”我可以猜到这个陈述似乎返回在评估值V后不会再次评估表达式,这就是为什么改变i不会影响返回值,纠正我。 - meexplorer
但我没有找到任何关于此的证据,其中提到返回不会再次评估表达式。 - meexplorer
@meexplorer有点晚了,但是在解释中 JLS 14.20.2。执行try-finally和try-catch-finally - 措辞有点复杂, 14.17。退货声明 也必须阅读 - Carlos Heuberger


这是一个详细说明 凯文的回答。重要的是要知道之前要评估要返回的表达式 finally,即使之后归还。

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

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

输出:

X
finally trumps return... sort of
0

80
2017-12-03 23:36



重要的是要知道。 - Aminadav Glickshtein


这就是最终块的整体想法。它允许您确保进行清理,否则可能会因为您返回而被忽略,当然。

终于被召唤了 不管发生了什么 在try块中(除非 你打电话 System.exit(int) 或Java虚拟机因其他原因而踢出)。


46
2018-05-13 06:19





考虑这一点的合理方式是:

  1. 必须执行放置在finally块中的代码 无论发生什么 在try块中
  2. 因此,如果try块中的代码尝试返回值或抛出异常,则将项放在“架子上”,直到finally块可以执行
  3. 因为finally块中的代码(按照定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,“货架上”留下的任何东西都将被丢弃。
  4. 唯一的例外是VM在try块期间完全关闭,例如通过'System.exit'

31
2017-09-15 19:26



这只是“考虑它的逻辑方式”,还是真正的最终块是如何根据规范工作的?在这里,Sun资源的链接将非常有趣。 - matias
第4点与之相矛盾 whatever happens 第1点的陈述 - ojonugwa ochalifu