考虑到这段代码,我能绝对确定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");
}
考虑到这段代码,我能绝对确定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");
}
当前回答
最后总是执行Block。除非和直到存在System.exit()语句(finally块中的第一条语句)。如果system.exit()是第一条语句,那么finally块将不会被执行,控制权将从finally块中释放出来。每当System.exit()语句进入finally块,直到该语句最终执行块,当System.exit)出现时,控制力完全从finally块中释放出来。
其他回答
最后总是执行Block。除非和直到存在System.exit()语句(finally块中的第一条语句)。如果system.exit()是第一条语句,那么finally块将不会被执行,控制权将从finally块中释放出来。每当System.exit()语句进入finally块,直到该语句最终执行块,当System.exit)出现时,控制力完全从finally块中释放出来。
以下是一些可以绕过finally块的条件:
如果在执行try或catch代码时JVM退出,那么finally块可能不会执行。更多太阳教程正常关闭-当最后一个非守护程序线程退出时,或者当Runtime.exit()(一些不错的博客)时,都会发生这种情况。当线程退出时,JVM会对正在运行的线程进行盘点,如果只剩下守护进程线程,则会启动有序关闭。当JVM停止时,所有剩余的守护程序线程都将被放弃。如果不执行块,堆栈也不会被解开,JVM只会退出。守护程序线程应该很少使用,很少有处理活动可以在任何时候安全地放弃,而无需清理。特别是,将守护程序线程用于可能执行任何类型I/O的任务是危险的。守护程序线程最好保存用于“内务管理”任务,例如后台线程,它定期从内存缓存中删除过期条目(源)
最后一个非守护程序线程退出示例:
public class TestDaemon {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("Is alive");
Thread.sleep(10);
// throw new RuntimeException();
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
System.out.println("This will never be executed.");
}
}
};
public static void main(String[] args) throws InterruptedException {
Thread daemon = new Thread(runnable);
daemon.setDaemon(true);
daemon.start();
Thread.sleep(100);
// daemon.stop();
System.out.println("Last non-daemon thread exits.");
}
}
输出:
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive
这就是最后一块的全部想法。当然,它可以让你确保你做了清理,否则可能会因为你回来而被跳过。
不管try块中发生了什么,最终都会被调用(除非您调用System.exit(int)或Java虚拟机因其他原因退出)。
公认的答案在几乎所有方面都是正确的,但它仍然只是事实的一半(好吧,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块始终正常终止,否则它是否始终被执行可能无关紧要…
考虑这一点的逻辑方法是:
放置在finally块中的代码必须在try块中执行因此,如果try块中的代码试图返回一个值或抛出一个异常,则该项将被“搁置”,直到finally块可以执行因为finally块中的代码(根据定义)具有高优先级,所以它可以返回或抛出任何它喜欢的东西。在这种情况下,“搁在架子上”的任何东西都会被丢弃。唯一的例外是,如果VM在try块期间完全关闭,例如通过“System.exit”