Java 中的finally 块总是被执行吗?
-
09-06-2019 - |
题
考虑到这段代码,我可以 绝对确定 认为 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
执行后会被调用 try
或者 catch
代码块。
仅有的几次 finally
不会被称为:
- 如果你调用
System.exit()
- 如果 JVM 先崩溃
- 如果 JVM 达到无限循环(或其他一些不可中断、非终止语句)
try
或者catch
堵塞 - 如果操作系统强制终止JVM进程;例如。,
kill -9 <pid>
在 UNIX 上 - 如果主机系统死机;例如,电源故障、硬件错误、操作系统恐慌等
- 如果
finally
块将由守护线程执行,所有其他非守护线程在此之前退出finally
叫做
其他提示
示例代码:
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
另外,尽管这是不好的做法,但如果 finally 块中有 return 语句,它将胜过常规块中的任何其他返回。也就是说,以下块将返回 false:
try { return true; } finally { return false; }
从finally 块中抛出异常也是如此。
以下是 Java 语言规范的官方措辞。
14.20.2。try-finally 和 try-catch-finally 的执行
A
try
声明与finally
块的执行是通过首先执行try
堵塞。然后就有一个选择:
- 如果执行
try
块正常完成,[...]- 如果执行
try
由于以下原因,块突然完成throw
具有一定的价值 V, [...]- 如果执行
try
由于任何其他原因块突然完成 右, ,那么finally
块被执行。然后就有一个选择:
- 如果finally块正常完成,那么
try
声明因原因而突然完成 右.- 如果
finally
由于某种原因块突然完成 S, ,那么try
声明因原因而突然完成 S (和理由 右 被丢弃).
规格为 return
实际上明确了这一点:
ReturnStatement: return Expression(opt) ;
A
return
声明没有Expression
尝试 将控制权转移到包含它的方法或构造函数的调用者。A
return
声明与Expression
尝试 将控制权转移给包含它的方法的调用者;的价值Expression
成为方法调用的值。前面的描述说“尝试 转移控制权“而不仅仅是”转移控制权“因为如果有的话
try
方法或构造函数中的语句try
块包含return
语句,然后任意finally
那些条款try
在控制权转移到方法或构造函数的调用者之前,语句将按从最内层到最外层的顺序执行。突然完成一个finally
条款可能会破坏由return
陈述。
除了其他响应之外,重要的是要指出“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();
}
}
我尝试了上面的例子,稍加修改 -
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。
这里有一个详细的阐述 凯文的回答. 。重要的是要知道要返回的表达式之前已计算过 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
这就是finally 块的全部思想。当然,它可以让您确保进行清理工作,否则这些清理工作可能会因为您返回而被跳过。
终于接到电话了 不管发生什么 在 try 块中(除非 你打电话 System.exit(int)
或者 Java 虚拟机由于某些其他原因而退出)。
思考这个问题的逻辑方法是:
- 必须执行位于finally 块中的代码 无论发生什么 在 try 块内
- 因此,如果 try 块中的代码尝试返回值或引发异常,则该项目将被放置“搁置”,直到 finally 块可以执行
- 因为finally块中的代码(根据定义)具有高优先级,所以它可以返回或抛出任何它喜欢的内容。在这种情况下,“架子上”留下的任何东西都会被丢弃。
- 唯一的例外是虚拟机在 try 块期间完全关闭,例如通过“系统退出”
除非程序异常终止(例如调用 System.exit(0)..),否则finally总是被执行。所以,你的系统输出将被打印
finally 块总是被执行,除非程序异常终止,无论是由于 JVM 崩溃还是调用 System.exit(0)
.
最重要的是,从finally块中返回的任何值都将覆盖执行finally块之前返回的值,因此在使用tryfinally时要小心检查所有退出点。
不,并非总是有一个例外情况是// 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 finally block
}finally{
System.out.println("Won't be executed");
System.out.println("No error");
}
}
}
最后总是运行,这就是重点,仅仅因为它出现在返回之后的代码中并不意味着这就是它的实现方式。Java 运行时有责任在退出时运行此代码 try
堵塞。
例如,如果您有以下情况:
int foo() {
try {
return 42;
}
finally {
System.out.println("done");
}
}
运行时会生成如下内容:
int foo() {
int ret = 42;
System.out.println("done");
return 42;
}
如果抛出未捕获的异常 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;
}
}
因为finally块总是会被调用,除非你调用 System.exit()
(或者线程崩溃)。
答案很简单 是的.
输入:
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
是的,它会被调用。这就是finally 关键字的全部意义所在。如果跳出 try/catch 块可以跳过 finally 块,则与将 System.out.println 放在 try/catch 之外是一样的。
简言之,在官方 Java 文档中(点击 这里),据记载——
如果JVM在执行尝试或捕获代码时退出,则最终块可能无法执行。同样,如果执行尝试或捕获代码的线程被中断或杀死,那么即使应用程序整体继续进行,最后块也可能无法执行。
是的,finally 块总是被执行。大多数开发人员使用此块来关闭数据库连接、结果集对象、语句对象,并且还使用 java hibernate 来回滚事务。
是的,它会。无论 try 或 catch 块中发生什么,除非调用 System.exit() 或 JVM 崩溃。如果块中有任何 return 语句,finally 将在该 return 语句之前执行。
是的,它会。唯一不会发生的情况是 JVM 退出或崩溃
考虑以下程序:
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.
}
}
这实际上在任何语言中都是如此......finally 总是在 return 语句之前执行,无论 return 位于方法体中的哪个位置。如果不是这样,finally 块就没有多大意义。
finally
将会执行,这是肯定的。
finally
以下情况不会执行:
情况1 :
当你执行时 System.exit()
.
案例2:
当你的 JVM/线程崩溃时。
案例3:
当您的执行手动停止时。
如果不处理异常,在终止程序之前,JVM会执行finally块。仅当程序正常执行失败意味着程序由于以下原因而终止时,它才不会执行。
通过导致致命错误导致进程中止。
由于内存损坏而终止程序。
通过调用 System.exit()
如果程序进入无限循环。
是的,因为 无控制语句 可以防止 finally
以免被处决。
这是一个参考示例,其中所有代码块都将被执行:
| x | Current result | Code
|---|----------------|------ - - -
| | |
| | | public static int finallyTest() {
| 3 | | int x = 3;
| | | try {
| | | try {
| 4 | | x++;
| 4 | return 4 | return x;
| | | } finally {
| 3 | | x--;
| 3 | throw | throw new RuntimeException("Ahh!");
| | | }
| | | } catch (RuntimeException e) {
| 4 | return 4 | return ++x;
| | | } finally {
| 3 | | x--;
| | | }
| | | }
| | |
|---|----------------|------ - - -
| | Result: 4 |
在下面的变体中, return x;
将被跳过。结果依然是 4
:
public static int finallyTest() {
int x = 3;
try {
try {
x++;
if (true) throw new RuntimeException("Ahh!");
return x; // skipped
} finally {
x--;
}
} catch (RuntimeException e) {
return ++x;
} finally {
x--;
}
}
当然,参考文献会跟踪它们的状态。此示例返回一个引用 value = 4
:
static class IntRef { public int value; }
public static IntRef finallyTest() {
IntRef x = new IntRef();
x.value = 3;
try {
return x;
} finally {
x.value++; // will be tracked even after return
}
}
try
- catch
- finally
是使用异常处理案例的关键词。
作为正常的解释
try {
//code statements
//exception thrown here
//lines not reached if exception thrown
} catch (Exception e) {
//lines reached only when exception is thrown
} finally {
// always executed when the try block is exited
//independent of an exception thrown or not
}
finally 块阻止执行...
- 当你打电话时
System.exit(0);
- 如果 JVM 退出。
- JVM 中的错误
添加到 @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
我尝试过,它是单线螺纹。
class Test {
public static void main(String args[]) throws Exception {
Object obj = new Object();
try {
synchronized (obj) {
obj.wait();
System.out.println("after wait()");
}
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
主线程将永远处于等待状态,因此finally永远不会被调用,
所以控制台输出不会打印字符串: after wait()
或者 finally
同意@Stephen C,上面的例子是第三个案例之一 这里:
在以下代码中添加更多这样的无限循环可能性:
// import java.util.concurrent.Semaphore;
class Test {
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 e) {
} finally {
System.out.println("finally");
}
}
}
案例2:如果 JVM 先崩溃
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Test {
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:如果finally块将由守护线程执行,并且所有其他非守护线程在调用finally之前退出。
class Test {
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”,这意味着“守护线程”中的“Finally block”没有执行
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