提问人:jonny five 提问时间:9/16/2008 最后编辑:Anshul Sharmajonny five 更新时间:7/5/2023 访问量:606241
finally 块总是在 Java 中执行吗?
Does a finally block always get executed in 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");
}
答:
是的,将在执行 or 代码块后调用。finally
try
catch
唯一不会被调用的时间是:finally
- 如果调用
System.exit()
- 如果调用
Runtime.getRuntime().halt(exitStatus)
- 如果 JVM 首先崩溃
- 如果 JVM 到达 or 块中的无限循环(或其他一些不可中断、非终止的语句)
try
catch
- 如果操作系统强行终止 JVM 进程;例如,在 UNIX 上
kill -9 <pid>
- 如果主机系统死机;例如,电源故障、硬件错误、操作系统崩溃等
- 如果该块将由守护进程线程执行,并且所有其他非守护进程线程在调用之前退出
finally
finally
评论
thread.stop()
finally
finally
try
finally
除非有异常程序终止(如调用),否则始终执行。所以,你的意志会被打印出来。System. exit(0)
System.out
是的,它会被调用。这就是拥有最终关键字的全部意义所在。如果跳出 try/catch 块可以跳过 finally 块,这与将 System.out.println 放在 try/catch 之外是一样的。
示例代码:
public static void main(String[] args) {
System.out.println(Test.test());
}
public static int test() {
try {
return 0;
}
finally {
System.out.println("something is printed");
}
}
输出:
something is printed.
0
评论
finally
return 2;
实际上,在任何语言中都是如此......finally 将始终在 return 语句之前执行,无论该返回位于方法体中的哪个位置。如果不是这样,最后的块就没有太大意义了。
除非存在异常程序终止,否则始终执行 finally 块,无论是由于 JVM 崩溃还是由于调用 .System.exit(0)
最重要的是,从 finally 块中返回的任何值都将覆盖在执行 finally 块之前返回的值,因此在使用 try finally 时要小心检查所有退出点。
此外,尽管这是不好的做法,但如果 finally 块中有 return 语句,它将胜过常规块的任何其他返回。也就是说,以下块将返回 false:
try { return true; } finally { return false; }
从最终块抛出异常也是如此。
评论
此外,最终返回将抛弃任何例外。http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
一个合乎逻辑的思考方式是:
- 无论 try 块中发生什么,都必须执行放置在 finally 块中的代码
- 因此,如果 try 块中的代码尝试返回值或抛出异常,则该项将被放置在“搁置”上,直到最后一个块可以执行
- 因为 finally 块中的代码(根据定义)具有很高的优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,任何留在“架子上”的东西都会被丢弃。
- 唯一的例外是,如果 VM 在 try 块期间完全关闭,例如通过“System.exit”
评论
除了最终替换 try 块中的返回时关于返回的要点之外,异常也是如此。抛出异常的 finally 块将替换从 try 块中抛出的返回或异常。
我尝试了上面的例子,略有修改-
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.");
}
}
上面的代码输出:
终于胜过了回归。
阿拉伯数字
这是因为 when is executed 的值为 2。在此之后,执行块,其中 12 被分配给,然后执行 out。return i;
i
finally
i
System.out
执行块后,块返回 2,而不是返回 12,因为此 return 语句不会再次执行。finally
try
如果你在 Eclipse 中调试这段代码,那么你会感觉到在执行 block 之后,block 的语句会再次执行。但事实并非如此。它只返回值 2。System.out
finally
return
try
评论
i
最后总是运行,这就是重点,仅仅因为它在返回后出现在代码中并不意味着这就是它的实现方式。Java 运行时有责任在退出块时运行此代码。try
例如,如果您有以下情况:
int foo() {
try {
return 42;
}
finally {
System.out.println("done");
}
}
运行时将生成如下内容:
int foo() {
int ret = 42;
System.out.println("done");
return 42;
}
如果抛出未捕获的异常,则该块将运行,并且异常将继续传播。finally
因为除非您调用(或线程崩溃),否则将始终调用 finally 块。System.exit()
这就是最终块的整个想法。它可以让你确保你做了清理,否则可能会因为你返回而被跳过,当然,还有其他事情。
无论 try 块中发生什么情况,最后都会被调用(除非您调用或 Java 虚拟机因其他原因踢出)。System.exit(int)
评论
除了其他响应之外,需要指出的是,“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();
}
}
评论
OutOfMemoryError
return retVal
finally
这是因为您将 i 的值指定为 12,但没有将 i 的值返回给函数。正确的代码如下:
public static int test() {
int i = 0;
try {
return i;
} finally {
i = 12;
System.out.println("finally trumps return.");
return i;
}
}
以下是 Java 语言规范中的官方用语。
14.20.2. try-finally 和 try-catch-finally 的执行
带有
finally
块的 try 语句通过首先执行try
块来执行。然后有一个选择:
- 如果区块的执行正常完成,[...]
try
- 如果由于值 V 的 a 而突然完成块的执行,[...]
try
throw
- 如果由于任何其他原因 R 而突然完成
try
块的执行,则执行final
块。然后有一个选择:
- 如果 finally 块正常完成,则语句因原因 R 而突然完成。
try
- 如果块由于原因 S 而突然完成,则语句因原因 S 而突然完成(并且原因 R 被丢弃)。
finally
try
实际上,规范对此进行了明确说明:return
ReturnStatement: return Expression(opt) ;
不尝试将控制权转移给包含它的方法或构造函数的调用方的语句。
return
Expression
尝试将控制权转移给包含该控制权的方法的调用方的语句;的值成为方法调用的值。
return
Expression
Expression
前面的描述说的是“尝试转移控制权”,而不仅仅是“转移控制权”,因为如果方法或构造函数中有任何语句,其块包含该语句,那么这些语句的任何子句都将按从最内层到最外层的顺序执行,然后再将控制权传递给方法或构造函数的调用者。条款的突然完成可能会中断由语句发起的控制权转移。
try
try
return
finally
try
finally
return
不,并非总是如此,在块阻止执行之前,会出现一种异常情况。System.exit(0);
finally
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 the finally block
} finally {
System.out.println("Won't be executed");
System.out.println("No error");
}
}
}
评论
是的,它会的。无论 try 或 catch 块中发生什么,除非 System.exit() 被调用或 JVM 崩溃。如果区块中有任何 return 语句,则 final 将在该 return 语句之前执行。
以下是凯文回答的详细说明。重要的是要知道要返回的表达式是在 之前计算的,即使它返回的时间在之后。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");
return 42;
}
}
输出:
X
finally trumps return... sort of
42
评论
finally
printX()
System.out.println("finally trumps return... sort of");
System.out.print("finally trumps return in try"); return 42;
return
printX()
return
try
catch
是的,它会的。 唯一不会的情况是JVM退出或崩溃
简明扼要地,在官方的 Java 文档(点击这里)中,它写道——
如果 JVM 在执行 try 或 catch 代码时退出,则 finally 块可能无法执行。同样,如果线程执行 try 或 catch 代码被中断或终止,最后的块可能会 即使整个应用程序继续,也不会执行。
是的,最后块总是被执行。大多数开发人员使用这个块来关闭数据库连接、resultset 对象、语句对象,并且还使用 into java 休眠来回滚事务。
finally
将执行,这是肯定的。
finally
在以下情况下不会执行:
案例 1 :
当您执行 .System.exit()
案例 2 :
当您的 JVM/线程崩溃时。
案例 3 :
当您的执行在两者之间手动停止时。
并非总是如此
Java 语言规范描述了 -- 和 - 块在 14.20.2
中的工作方式,它没有在任何地方指定始终执行块。
但是,对于 -- 和 - 块完成的所有情况,它确实指定了在完成之前必须执行。try
catch
finally
try
catch
finally
try
catch
finally
try
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 不保证 FIN 在 CODE 之后执行。 JLS 保证如果执行 CODE 和 NEXT,则 FIN 将始终在 CODE 之后和 NEXT 之前执行。
为什么 JLS 不能保证区块总是在区块之后执行?因为这是不可能的。JVM 不太可能,但有可能在完成块后但在块执行之前被中止(终止、崩溃、关机)。JLS无法避免这种情况。finally
try
try
finally
因此,任何软件的正常行为都依赖于块的执行,总是在块完成后执行。finally
try
return
块中的说明与此问题无关。如果执行到达代码之后 -- 则保证该块在块内有或没有指令之前就已经执行了。try
try
catch
finally
finally
return
try
评论
any software which for their proper behaviour depends on finally blocks always being executed after their try blocks complete are bugged.
finally
Error
finally
我试过了这个, 它是单线程的。
public static void main(String args[]) throws Exception {
Object obj = new Object();
try {
synchronized (obj) {
obj.wait();
System.out.println("after wait()");
}
} catch (Exception ignored) {
} finally {
System.out.println("finally");
}
}
将永远处于状态,因此最终
永远不会被召唤,main
Thread
wait
所以控制台输出不会 : after 或print
String
wait()
finally
与@Stephen C 一致,上面的例子是这里提到的第 3 种情况之一:
在下面的代码中添加更多这样的无限循环可能性:
// import java.util.concurrent.Semaphore;
public static void main(String[] args) {
try {
// Thread.sleep(Long.MAX_VALUE);
// Thread.currentThread().join();
// new Semaphore(0).acquire();
// while (true){}
System.out.println("after sleep join semaphore exit infinite while loop");
} catch (Exception ignored) {
} finally {
System.out.println("finally");
}
}
案例 2:如果 JVM 首先崩溃
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public static void main(String args[]) {
try {
unsafeMethod();
//Runtime.getRuntime().halt(123);
System.out.println("After Jvm Crash!");
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
unsafe.putAddress(0, 0);
}
参考:你如何使JVM崩溃?
情况 6:如果 block 将由 daemon 执行,并且调用了 before 所有其他非守护进程 exit。finally
Thread
Threads
finally
public static void main(String args[]) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
printThreads("Daemon Thread printing");
// just to ensure this thread will live longer than main thread
Thread.sleep(10000);
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
};
Thread daemonThread = new Thread(runnable);
daemonThread.setDaemon(Boolean.TRUE);
daemonThread.setName("My Daemon Thread");
daemonThread.start();
printThreads("main Thread Printing");
}
private static synchronized void printThreads(String str) {
System.out.println(str);
int threadCount = 0;
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (Thread t : threadSet) {
if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
System.out.println("Thread :" + t + ":" + "state:" + t.getState());
++threadCount;
}
}
System.out.println("Thread count started by Main thread:" + threadCount);
System.out.println("-------------------------------------------------");
}
输出:这不会打印“finally”,这意味着“守护进程线程”中的“最后块”未执行
main Thread Printing Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED Thread :Thread[main,5,main]:state:RUNNABLE Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE Thread count started by Main thread:3 ------------------------------------------------- Daemon Thread printing Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE Thread count started by Main thread:2 ------------------------------------------------- Process finished with exit code 0
评论
答案很简单:是的。
输入:
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
评论
补充@vibhash的答案,因为没有其他答案可以解释在可变对象的情况下会发生什么,如下所示。
public static void main(String[] args) {
System.out.println(test().toString());
}
public static StringBuffer test() {
StringBuffer s = new StringBuffer();
try {
s.append("sb");
return s;
} finally {
s.append("updated ");
}
}
将输出
sbupdated
评论
请考虑以下程序:
public class SomeTest {
private static StringBuilder sb = new StringBuilder();
public static void main(String args[]) {
System.out.println(someString());
System.out.println("---AGAIN---");
System.out.println(someString());
System.out.println("---PRINT THE RESULT---");
System.out.println(sb.toString());
}
private static String someString() {
try {
sb.append("-abc-");
return sb.toString();
} finally {
sb.append("xyz");
}
}
}
从 Java 1.8.162 开始,上面的代码块给出了以下输出:
-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz
这意味着使用释放对象是一种很好的做法,如以下代码所示:finally
private static String someString() {
StringBuilder sb = new StringBuilder();
try {
sb.append("abc");
return sb.toString();
} finally {
sb = null; // Just an example, but you can close streams or DB connections this way.
}
}
评论
sb.setLength(0)
sb = null;
finally
System.out.println("---AGAIN2---");
System.out.println(sb);
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
x foo() 的值 = 2
评论
finally
finalize()