考虑到这段代码,我能绝对确定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");
}

当前回答

公认的答案在几乎所有方面都是正确的,但它仍然只是事实的一半(好吧,95%的事实)。

假设以下代码:

private final Lock m_Lock = new ReentrantLock();
…
public final SomeObject doSomething( final SomeObject arg )
{
  final SomeObject retValue;
  try
  {
    lock.lock();
    retValue = SomeObject( arg );
  }
  finally
  {
    out.println( "Entering finally block");
    callingAnotherMethod( arg, retValue );
    lock.unlock();
  }
  
  return retValue;
}
…
try
{
   final var result = doSomething( new SomeObject() );
}
catch( final StackOverflowError e ) { /* Deliberately ignored */ }

调用doSomething()方法将几乎立即导致StackOverflowError。

锁也不会松开!

但是,当finally块总是被执行时(接受的答案中已经列出了例外情况),这怎么会发生呢?

这是因为不能保证finally块中的所有语句都真正执行!

如果在调用lock.unlock()之前调用System.exit()或throws语句,这将是显而易见的。

但示例代码中没有类似的内容…

除此之外,调用lock.unlock()之前finally块中的另外两个方法调用将导致另一个StackOverflowError…

瞧,锁没有松开!

虽然这样的示例代码很愚蠢,但在许多类型的软件中都可以找到类似的模式。只要最后一个街区没有发生什么丑恶的事情,一切都会好起来的…

有趣的事实是,它在Java的更高版本中不起作用(这意味着在更高的版本中,锁被释放了…)。不知道何时以及为什么会发生变化。

但您仍然必须确保finally块始终正常终止,否则它是否始终被执行可能无关紧要…

其他回答

这是Java语言规范中的官方词汇。

14.20.2.执行try finally和try catch finally

带有finally块的try语句通过首先执行try块来执行。然后有一个选择:如果try块的执行正常完成,[…]如果try块的执行因抛出值V而突然完成,[…]如果由于任何其他原因R,try块的执行突然完成,则执行finally块。然后有一个选择:如果finally块正常完成,则try语句由于原因R而突然完成。如果finally块因原因S而突然完成,则try语句因原因S突然完成(并且原因R被丢弃)。

返回的规范实际上明确了这一点:

JLS 14.17退货声明

退货声明:返回表达式(opt);没有表达式的return语句试图将控制传递给包含它的方法或构造函数的调用者。带有表达式的return语句试图将控制权传递给包含它的方法的调用者;表达式的值变为方法调用的值。前面的描述说的是“试图传递控制”而不仅仅是“传递控制”,因为如果在方法或构造函数中有任何try语句,其try块包含return语句,那么在将控制传递给方法或构造函数的调用程序之前,这些try语句的任何finally子句都将按从内到外的顺序执行。finally子句的突然完成可能会中断由return语句发起的控制权转移。

简而言之,在官方Java文档(单击此处)中-

如果JVM在执行try或catch代码时退出,则finally块可能不执行。同样,如果线程执行try-or-catch代码被中断或终止,finally块可能即使应用程序作为一个整体继续,也不会执行。

finally块总是在返回x(计算)值之前执行。

System.out.println("x value from foo() = " + foo());

...

int foo() {
  int x = 2;
  try {
    return x++;
  } finally {
    System.out.println("x value in finally = " + x);
  }
}

输出:

最终x值=3来自foo()的x值=2

我很晚才回答,但我很惊讶没有人提到Java调试器选项来删除堆栈帧。我是IntelliJ中此功能的重度用户。(我确信Eclipse和NetBeans支持相同的功能。)

如果我从后面跟着finally块的try或catch块中删除堆栈帧,IDE将提示我:“我要执行finally块吗?”显然,这是一个人工运行时环境——调试器!

为了回答您的问题,我想说,只有在连接调试器时忽略,并且(就像其他人所说的)method something()不(a)通过JNI调用Java方法System.exit(int)或(b)C函数exit(int/abort(),或(C)对自身执行类似于调用kill-9$PID(!)的疯狂操作时,才能保证它运行。

最后总是执行Block。除非和直到存在System.exit()语句(finally块中的第一条语句)。如果system.exit()是第一条语句,那么finally块将不会被执行,控制权将从finally块中释放出来。每当System.exit()语句进入finally块,直到该语句最终执行块,当System.exit)出现时,控制力完全从finally块中释放出来。