finally块总是在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."); }
是的, finally
将在执行try或catch代码块之后调用。
finally
不会被召唤的唯一时间是:
- 如果你调用
System.exit()
; - 如果JVM首先崩溃;
- 如果JVM在
try
或catch
块中到达无限循环(或其他一些不可中断的,非终止语句); - 如果操作系统强行终止JVM进程; 例如UNIX上的“kill -9”。
- 如果主机系统死亡; 例如电源故障,硬件错误,操作系统恐慌等。
- 如果最终块将由守护程序线程执行,并且所有其他非守护程序线程在最终被调用之前退出。
示例代码:
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块中抛出exception也是一样的。
这是Java语言规范中的官方文字。
14.20.2。 执行try-finally和try-catch-finally
通过首先执行
try
块来执行具有finally
块的try
语句。 然后有一个选择:
- 如果
try
块的执行正常完成,[…]- 如果由于
throw
值V而突然完成try
块的执行,[…]- 如果
try
块的执行由于任何其他原因R突然完成,则执行finally
块。 然后有一个选择:
- 如果finally块正常完成,那么
try
语句突然完成,原因是R。- 如果
finally
块因为原因S而突然完成,则try
语句突然完成,原因是S ( 并且原因R被丢弃 )。
return
的规范实际上使这个明确:
JLS 14.17返回声明
ReturnStatement: return Expression(opt) ;
没有
Expression
return
语句尝试将控制权转移给包含它的方法或构造函数的调用者。带有
Expression
return
语句尝试将控制权转移给包含它的方法的调用者;Expression
的值成为方法调用的值。前面的描述说“ 尝试转移控制 ”而不仅仅是“ 转移控制 ”,因为如果在
try
块中包含return
语句的方法或构造函数中有任何try
语句,那么将执行那些try
语句的任何finally
子句,在将控制权转移给方法或构造函数的调用者之前,从最里面到最外面的顺序。 突然完成finally
子句可以破坏由return
语句启动的控制转移。
除了其他响应之外,重要的是要指出’finally’有权通过try..catch块覆盖任何exception/返回值。 例如,以下代码返回12:
public static int getMonthsInYear() { try { return 10; } finally { return 12; } }
同样,以下方法不会抛出exception:
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
块返回2,而不是返回12,因为这个return语句不会再次执行。
如果您将在Eclipse中调试此代码,那么您会感觉在执行finally
块的System.out
,再次执行try
块的return
语句。 但这种情况并非如此。 它只返回值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
这就是最终块的整体想法。 它允许您确保进行清理,否则可能会因为您返回而被忽略,当然。
无论 try块中发生了什么,最终都会被调用( 除非你调用System.exit(int)
或Java虚拟机因其他原因而踢出)。
考虑这一点的合理方式是:
- 放置在finally块中的代码必须在try块内发生
- 因此,如果try块中的代码尝试返回值或抛出exception,则将项放在“架子上”,直到finally块可以执行
- 因为finally块中的代码(按照定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。 在这种情况下,“货架上”留下的任何东西都将被丢弃。
- 唯一的例外是VM在try块期间完全关闭,例如’System.exit’
最后的回报也会抛弃任何exception。 http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
除非程序exception终止(如调用System.exit(0)..),否则最后总是执行。 所以,你的sysout会打印出来
不,并不总是一个例外情况是// 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"); } } }
除非由于JVM崩溃或调用System.exit(0)
导致程序exception终止,否则始终执行finally块。
最重要的是,finally块中返回的任何值都将覆盖执行finally块之前返回的值,因此在使用try finally时要小心检查所有出口点。
最后总是运行这是整点,只是因为它在返回后出现在代码中并不意味着它是如何实现的。 退出try
块时,Java运行时负责运行此代码。
例如,如果您有以下内容:
int foo() { try { return 42; } finally { System.out.println("done"); } }
运行时将生成如下内容:
int foo() { int ret = 42; System.out.println("done"); return 42; }
如果抛出未捕获的exception,则finally
块将运行,exception将继续传播。
因为除非你调用System.exit()
(或线程崩溃),否则将始终调用finally块。
简而言之,在官方Java文档(点击这里 )中,写道 –
如果在执行try或catch代码时JVM退出,则finally块可能无法执行。 同样,如果执行try或catch代码的线程被中断或终止,则即使应用程序作为一个整体继续,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关键字的重点。 如果跳出try / catch块可能只是跳过finally块,那就像将System.out.println放在try / catch之外一样。
是的,它会。 无论你的try或catch块发生了什么,除非调用System.exit()或JVM崩溃。 如果块中有任何return语句,则最终将在该return语句之前执行。
是的,它会。 只有JVM退出或崩溃才会出现这种情况
是的,最后块总是执行。 大多数开发人员使用此块关闭数据库连接,结果集对象,语句对象以及使用java hibernate来回滚事务。
答案很简单。
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"); }
OUTPUT:
catch finally
这在任何语言中都是正确的…最终将始终在return语句之前执行,无论方法体中返回的位置如何。 如果不是这样的话,那么finally块就没有多大意义了。
因为无论你有什么案例,总是会被调用。 你没有exception,它仍然被调用,捕获exception,它仍然被调用
在正常的执行过程中考虑这一点(即没有抛出任何exception):如果方法不是’void’,那么它总是显式地返回一些东西,但是,最后总是被执行
如果抛出exception,最后运行。 如果没有抛出exception,最后运行。 如果捕获到exception,则最终运行。 如果未捕获exception,则最终运行。
只有当JVM退出时它才会运行。
最后块总是执行是否处理exception。如果在try块之前发生任何exception,则finally块将不会执行。
考虑这一点的合理方式是:
放置在finally块中的代码必须在try块内发生。
因此,如果try块中的代码尝试返回一个值或抛出一个exception,那么该项将被置于“架子上”,直到finally块可以执行为止。因为finally块中的代码具有(按照定义)高优先级,它可以返回或抛出无论喜欢什么。 在这种情况下,“货架上”留下的任何东西都将被丢弃。
唯一的例外是VM在try块期间完全关闭,例如’System.exit’
永远不要从finally块中抛出任何exception
try { someMethod(); //Throws exceptionOne } finally { cleanUp(); //If finally also threw any exception the exceptionOne will be lost forever }
这很好,只要cleanUp()永远不会抛出任何exception。 在上面的示例中,如果someMethod()抛出exception,并且在finally块中,cleanUp()也会抛出exception,第二个exception将来自方法,并且原始的第一个exception(正确的原因)将永远丢失。 如果您在finally块中调用的代码可能会抛出exception,请确保您要么处理它,要么记录它。 永远不要让它从最后一块出来。
实际上退出程序(通过调用System.exit()或导致导致进程中止的致命错误:有时在Windows中非正式地称为“热点”或“Dr Watson”)将阻止您的finally块被阻止执行!
没有什么可以阻止我们嵌套try / catch / finally块(例如,将try / finally块放在try / catch块中,反之亦然),这并不是一件不寻常的事情。
考虑以下程序:
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()); } private static String someString() { try { sb.append("-abc-"); return sb.toString(); } finally { sb.append("xyz"); } } }
从Java 1.8.162开始,上面的代码块给出了以下输出:
-abc- ---AGAIN--- -abc-xyz-abc-
这意味着finally
使用释放对象是一个很好的做法,如下面的代码:
private static String someString() { StringBuilder sb = new StringBuilder(); try { sb.append("abc"); return sb.toString(); } finally { sb = null; } }
除了最后替换try块中的返回的返回点之外,exception也是如此。 抛出exception的finally块将替换try块内抛出的返回或exception。