Java中的finally块是否总是会被执行?

2684
考虑到这段代码,我能否绝对确定无论 something() 是什么,finally 语句块总是会被执行?
try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}

61
不总是。 - Boann
3
《Effective Java》认为情况并非如此。http://www.informit.com/articles/article.aspx?p=1216151&seqNum=7 - Binoy Babu
36
@BinoyBabu,_finalizer_不等于finally;_finalizer_指的是finalize()方法。 - jaco0646
4
没错,“不总是”是正确的。但是这样你就永远不能使用“保证”或“总是”这些词了。 - MC Emperor
2
@Boann 我会这样表达:在跳出 try-finally 结构之前,执行流程总是会经过 finally。如果它在其中失败了,我也没关系,因为 finally 的主要目的是确保其他代码部分不会出现混乱。 - Imperishable Night
显示剩余3条评论
52个回答

19
并不总是这样。有一种例外情况是在finally块被执行之前,使用System.exit(0)语句会阻止finally块的执行。
  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

这就是为什么你绝对不应该调用System.exit()的原因之一... - Franz D.

18

无论是由JVM崩溃还是调用System.exit(0)导致的异常程序终止,finally块始终会被执行。

另外,任何在finally块中返回的值都会覆盖在finally块执行之前返回的值,因此在使用try finally时要小心检查所有退出点。


18

12

finally块总是会被执行,这是其存在的意义所在。即使它出现在return语句之后,也不代表它就是按照这种顺序实现的。Java运行时有责任在退出try块时运行此代码。

例如,如果您有以下代码:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

运行时会生成类似于以下内容:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

如果有未被捕获的异常被抛出,finally 块将会运行,并且异常会继续传播。


11

并非总是

Java语言规范在14.20.2处描述了try-catch-finallytry-catch块的工作原理。

在任何地方都没有指定finally块总是被执行。

但对于所有try-catch-finallytry-finally块完成的情况,在完成之前它确实指定了必须先执行finally

try {
  CODE inside the try block
}
finally {
  FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).

JLS不能保证在CODE之后一定执行FIN,但它保证如果执行了CODENEXT,那么FIN将始终在CODE之后且在NEXT之前执行。

为什么JLS不能保证finally块总是在try块之后执行?因为这是不可能的。 虚拟机可能会在完成try块后但在执行finally块之前被中止(杀死,崩溃,关机),尽管这不太可能发生。对此JLS无能为力。

因此,任何软件如果需要finally块始终在try块完成后执行才能正确运行,则存在缺陷。

try块中的return指令与此问题无关。如果执行到try-catch-finally之后的代码,则保证已经在try块内部是否有return指令之前执行了finally块。


非常抱歉评论一个如此陈旧的答案,但我认为说“任何软件都依赖于在其 try 块完成后始终执行 finally 块才能实现其正确行为”是不正确的。通常使用 finally 块进行资源清理以防止泄漏。否则你会怎么做呢?同样,您也不能(或者不应该……)捕获 Error,因为正常应用程序通常没有合理的处理方式。 - filpa
如果finally块中的清理是关于关闭一个打开的文件的示例,那么不执行finally块不是问题,操作系统会在程序终止时关闭它。如果清理是删除文件,则存在问题,如果程序在finally之前被中止,则操作系统不会执行该操作。如果系统的另一部分依赖于Java程序始终执行此类删除,则系统总体和特别是Java程序存在错误。您不能依赖于finally始终执行。您只能依赖于保证如果NEXT已经执行,则finally已经执行。 - Anonymous Coward
有趣的看法,尽管据我所见它强制了你的设计的某些部分。针对你删除示例的进一步扩展:你会运行一种负责文件删除(清理)的“回收者”任务吗?我承认,在你的解释中,使用finally可能(经过足够的时间,很可能会)导致错误,但是额外的开发(和测试)时间是否值得潜在的几乎不可察觉的稳定性改善呢?更一般地说:应该投入到特定设计中的关注程度是否取决于涉及的子系统?毕竟时间是宝贵的资源。 :) - filpa
1
@user991710 使用finally没有任何问题。您只需要知道它的作用和不作用。对于删除文件的特定情况,我实际上会在finally块中执行。但是要注意,这样做并不能提供任何保证(即使try块的最后一行执行完毕)。因此,在执行程序的第一步中删除以前遗留的文件,如果这是一个将自动重新运行的服务式程序,则应采取预防措施来处理这种情况。如果在这种情况下系统故障不成问题,那么当然不必费心。 - Anonymous Coward

10

Answer is simple YES.

INPUT:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e) {
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

输出:

catch
finally

2
答案很简单,不。 - Christophe Roussy
1
@ChristopheRoussy 怎么做?你能解释一下吗? - Meet Vora
1
请阅读被接受的答案,原问题是关于“它是否总是会执行”,但实际上并不总是如此。在您的情况下可能会如此,但这并不能回答原问题,甚至可能会误导初学者。 - Christophe Roussy
那么在哪种情况下它不会被执行? - Meet Vora
在其他答案中提到的所有情况中,请查看获得1000多个赞同的被接受的答案。 - Christophe Roussy

10

这是因为你将i的值赋为12,但没有将i的值返回给函数。正确的代码如下:

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

10

由于无论如何,除非您调用System.exit()(或线程崩溃),否则finally块将始终被调用。


10
简洁地说,在官方Java文档中(点击这里),写道-如果JVM在执行try或catch代码时退出,则finally块可能不会执行。同样,如果执行try或catch代码的线程被中断或终止,则finally块可能不会执行,即使整个应用程序仍在运行。

10

是的,finally块中的代码将会被执行。这也是使用finally关键字的主要目的。如果跳出try/catch块可以直接跳过finally块,那么就相当于将System.out.println语句放在try/catch块之外。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接