考虑到这段代码,我是否可以绝对确定该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");
}
是的,finally
将在try
或catch
代码块执行后调用。
唯一finally
不会被调用的时间是:
System.exit()
Runtime.getRuntime().halt(exitStatus)
try
如果 JVM 在orcatch
块中到达无限循环(或其他一些不可中断、非终止的语句)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("something is printed");
}
}
输出:
something is printed.
0
此外,虽然这是不好的做法,但如果 finally 块中有 return 语句,它将胜过常规块的任何其他 return。也就是说,以下块将返回 false:
try { return true; } finally { return false; }
从 finally 块中抛出异常也是如此。
这是 Java 语言规范中的官方用语。
14.20.2。try-finally 和 try-catch-finally 的执行
通过首先执行块来执行
try
带有块的语句。然后有一个选择:finally
try
- 如果
try
块的执行正常完成,[...]- 如果块的执行由于值V
try
而突然完成,[...]throw
- 如果块的执行
try
由于任何其他原因R突然完成,则执行该finally
块。然后有一个选择:
- 如果 finally 块正常完成,则
try
语句由于原因R突然完成。- 如果
finally
块由于原因S突然完成,则try
语句由于原因S突然完成(并且原因R被丢弃)。
的规范return
实际上明确了这一点:
ReturnStatement: return Expression(opt) ;
不尝试将控制权转移给包含它的方法或构造函数的调用者的语句
return
。Expression
试图将控制权转移给包含它的方法的调用者的语句
return
;的值成为方法调用的值。Expression
Expression
前面的描述说“尝试转移控制”而不仅仅是“转移控制
try
”,因为如果方法或构造函数中有任何语句的try
块包含该return
语句,那么finally
这些语句的任何子句都try
将按从内到外的顺序执行,在将控制权转移到方法或构造函数的调用者之前。一个子句的突然完成finally
可能会中断由return
语句启动的控制转移。
除了其他响应之外,重要的是要指出“终于”有权覆盖 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
这是因为 when return i;
is executedi
的值为 2。在此之后finally
执行块,其中分配了 12 i
,然后System.out
执行 out。
执行完finally
块后,try
块返回 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 总是被执行,除非有异常的程序终止(比如调用 System.exit(0)..)。所以,你的系统输出将被打印出来
不,并非总是一种例外情况是// 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");
}
}
}
finally 块总是被执行,除非程序异常终止,要么是由于 JVM 崩溃,要么是由于调用System.exit(0)
.
最重要的是,从 finally 块中返回的任何值都将覆盖在 finally 块执行之前返回的值,因此在使用 try finally 时要小心检查所有退出点。
finally 中的 return 也会抛出任何异常。 http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
finally 总是 run 这就是重点,仅仅因为它出现在 return 之后的代码中并不意味着它就是这样实现的。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 关键字的全部意义所在。如果跳出 try/catch 块可以跳过 finally 块,这与将 System.out.println 放在 try/catch 之外是一样的。
System.exit()
因为除非您调用(或线程崩溃),否则将始终调用 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 文档(单击此处)中,写到 -
如果在执行 try 或 catch 代码时 JVM 退出,则 finally 块可能不会执行。同样,如果执行 try 或 catch 代码的线程被中断或杀死,即使应用程序作为一个整体继续运行,finally 块也可能不会执行。
Java 语言规范在14.20.2try
中描述了-和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 不保证finally
块总是在try
块之后执行?因为这是不可能的。try
在完成块之后但在执行块之前,JVM 不太可能但可能会中止(杀死、崩溃、关闭电源)finally
。JLS 无法避免这种情况。
因此,任何在其块完成后总是依赖于finally
块的正确行为的软件都会被try
窃听。
return
块中的指令try
与此问题无关。如果执行到达try
- catch
-之后的代码finally
,则保证该finally
块之前已经执行过,无论块return
内是否有指令try
。
答案很简单YES。
输入:
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
块总是在返回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);
}
}
输出:
finally 中的 x 值 = 3
x 来自 foo() 的值 = 2
是的,它会。无论您的 try 或 catch 块中发生什么,除非另有 System.exit() 调用或 JVM 崩溃。如果块中有任何 return 语句,finally 将在该 return 语句之前执行。
是的,它会。唯一的情况是 JVM 退出或崩溃
是的,finally 块总是执行。大多数开发人员使用此块来关闭数据库连接、结果集对象、语句对象,并使用 java hibernate 来回滚事务。
finally
将执行,这是肯定的。
finally
在以下情况下不会执行:
情况1 :
当你正在执行System.exit()
.
案例2:
当您的 JVM / 线程崩溃时。
案例3:
当您手动停止执行时。
我试过这个,它是单线程的。
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
状态,因此finally
永远不会被调用,
所以控制台输出不会print
String
:在wait()
或之后finally
同意@Stephen C,上面的例子是这里提到的第三种情况之一:
在以下代码中添加更多这样的无限循环可能性:
// 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:如果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”,这意味着“daemon thread”中的“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
添加到@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.
}
}
这实际上在任何语言中都是正确的……finally 总是在 return 语句之前执行,无论 return 在方法体中的什么位置。如果不是这样,finally 块就没有多大意义。
除了关于 return 在 try 块中最终替换 return 的点之外,异常也是如此。抛出异常的 finally 块将替换从 try 块中抛出的返回或异常。
我对不同论坛上提供的所有答案感到非常困惑,并决定最终编码并查看。输出是:
即使在 try 和 catch 块中有 return,finally 也会被执行。
try {
System.out.println("try");
return;
//int i =5/0;
//System.exit(0 ) ;
} catch (Exception e) {
System.out.println("catch");
return;
//int i =5/0;
//System.exit(0 ) ;
} finally {
System.out.println("Print me FINALLY");
}
输出
尝试
最后打印我
System.exit(0)
try 和 catch 块,并且在它之前发生异常,无论出于何种原因。如果不处理异常,在终止程序之前,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);
如果抛出异常,finally 运行。如果没有抛出异常,finally 运行。如果异常被捕获,finally 运行。如果没有捕获到异常,finally 运行。
只有当 JVM 退出时它不运行。
在几个独特的场景中,在 return 之后不会调用 finally 块:如果首先调用 System.exit(),或者如果 JVM 崩溃。
让我尝试以最简单的方式回答您的问题。
规则 1:finally 块总是运行(虽然有例外。但让我们坚持一段时间。)
规则 2:finally 块中的语句在控制离开 try 或 catch 块时运行。控制的转移可以作为正常执行、break 、 continue、goto 或 return 语句的执行,或 a传播异常。
在返回语句的情况下(因为它的标题),控件必须离开调用方法,因此调用相应的 try-finally 结构的 finally 块。return 语句在 finally 块之后执行。
如果 finally 块中也有 return 语句,它肯定会覆盖 try 块中未决的语句,因为它清除了调用堆栈。
您可以在这里参考更好的解释:http: //msdn.microsoft.com/en-us/ .... 这个概念在所有高级语言中基本相同。
是的,写在这里
如果在执行 try 或 catch 代码时 JVM 退出,则 finally 块可能不会执行。同样,如果执行 try 或 catch 代码的线程被中断或杀死,即使应用程序作为一个整体继续运行,finally 块也可能不会执行。
试试这段代码,你会明白finally 块中的代码是在 return 语句之后执行的。
public class TestTryCatchFinally {
static int x = 0;
public static void main(String[] args){
System.out.println(f1() );
System.out.println(f2() );
}
public static int f1(){
try{
x = 1;
return x;
}finally{
x = 2;
}
}
public static int f2(){
return x;
}
}
无论是否处理异常,finally 块始终执行。如果在 try 块之前发生任何异常,则 finally 块将不会执行。
因为无论你有什么情况,总决赛都会被调用。你没有异常,它仍然被调用,捕获异常,它仍然被调用
在正常的执行过程中考虑这一点(即没有抛出任何异常):如果方法不是“void”,那么它总是显式地返回一些东西,然而,最终总是被执行
finally
如果 an 在嵌套块Exception
中为throw
n ,也可以提前退出。编译器将警告您该块未正常完成或给出您有无法访问的代码的错误。仅当不在条件语句或循环内时才会显示无法访问代码的错误。finally
finally
throw
try{
}finally{
try{
}finally{
//if(someCondition) --> no error because of unreachable code
throw new RunTimeException();
}
int a = 5;//unreachable code
}
与以下代码相同:
static int f() {
while (true) {
try {
return 1;
} finally {
break;
}
}
return 2;
}
f 将返回 2!
是的,它总是会被调用,但在一种情况下,当你使用 System.exit() 时它不会调用
try{
//risky code
}catch(Exception e){
//exception handling code
}
finally(){
//It always execute but before this block if there is any statement like System.exit(0); then this block not execute.
}
即使您在 try 块中放置了 return 语句,finally 块也会始终执行。finally 块将在 return 语句之前执行。
finally 总是在最后被调用
当你尝试时,它会执行一些代码,如果在 try 中发生了某些事情,那么 catch 将捕获该异常并且你可以打印一些 mssg 或抛出错误,然后执行 finally 块。
finally 通常在进行清理时使用,例如,如果您在 java 中使用扫描仪,您可能应该关闭扫描仪,因为它会导致其他问题,例如无法打开某些文件
以下是一些可以绕过 finally 块的条件:
最后一个非守护线程退出示例:
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
尝试资源示例
static class IamAutoCloseable implements AutoCloseable {
private final String name;
IamAutoCloseable(String name) {
this.name = name;
}
public void close() {
System.out.println(name);
}
}
@Test
public void withResourceFinally() {
try (IamAutoCloseable closeable1 = new IamAutoCloseable("closeable1");
IamAutoCloseable closeable2 = new IamAutoCloseable("closeable2")) {
System.out.println("try");
} finally {
System.out.println("finally");
}
}
测试输出:
try
closeable2
closeable1
finally
我在这里回答太晚了,但我很惊讶没有人提到 Java 调试器选项来删除堆栈帧。我是 IntelliJ 中此功能的重度用户。(我确信Eclipse 和 NetBeans 支持相同的功能。)
如果我从 try 或 catch 块中删除堆栈帧,然后是 finally 块,IDE 将提示我:“我应该执行 finally 块吗?” 显然,这是一个人工的运行时环境——一个调试器!
要回答你的问题,我想说你只能保证它在附加调试器时忽略它运行,并且(就像其他人所说)方法something()
不(a)调用 Java 方法System.exit(int)
或(b)C 函数exit(int)
/abort()
通过 JNI 或(c)做一些疯狂的事情,比如kill -9 $PID
自己打电话(!)。
公认的答案几乎在所有方面都是正确的,但它仍然只是一半的真相(好吧,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
几乎立即导致。
并且lock
不会被释放!
finally
但是,当块总是被执行(除了已在接受的答案中列出的例外)时,怎么会发生这种情况呢?
那是因为无法保证块中的所有语句都真正执行!finally
System.exit()
如果在调用之前有调用或throws
语句,这将是显而易见的lock.unlock()
。
但是示例代码中没有这样的东西……</p>
finally
除了调用之前块中的另外两个方法调用lock.unlock()
将导致另一个StackOverflowError
......</p>
瞧,锁没有被释放!
尽管这样的示例代码很愚蠢,但在许多软件中都可以找到很多类似的模式。只要finally
块中没有发生任何丑陋的事情,一切都可以正常工作......</p>
有趣的事实是它在 Java 的更高版本中不起作用(意味着在更高版本中,锁被释放了……)。不知道何时以及为什么会发生这种变化。
但是你仍然必须确保finally
块总是正常终止,否则它是否总是被执行可能并不重要......</p>