it-swarm.cn

Finally块总是在Java中执行吗?

考虑到这段代码,无论something()是什么,我都可以 绝对确定 finally块总是执行吗?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}
2159
jonny five

是的,finally将在执行trycatch代码块后被调用。

唯一不会调用finally的是:

  1. 如果你调用System.exit()
  2. 如果JVM首先崩溃
  3. 如果JVM在trycatch块中到达无限循环(或其他一些不可中断的,非终止语句)
  4. 如果操作系统强行终止JVM进程;例如,UNIX上的kill -9 <pid>
  5. 如果主机系统死机;例如,电源故障,硬件错误,OS恐慌等
  6. 如果finally块将由守护程序线程执行,并且所有其他非守护程序线程在finally被调用之前退出
2415
jodonnell

示例代码:

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
502
Kevin

此外,虽然这是不好的做法,但如果在finally块中有一个return语句,它将胜过常规块中的任何其他返回。也就是说,以下块将返回false:

try { return true; } finally { return false; }

从finally块中抛出异常也是一样的。

362
MooBob42

这是Java语言规范中的官方文字。

14.20.2。执行try-finally和try-catch-finally

首先执行try块,执行带finally块的try语句。然后有一个选择:

  • 如果try块的执行正常完成,[...]
  • 如果try块的执行由于Vthrow而突然完成,[...]
  • 如果由于任何其他原因R突然执行try块,则执行finally块。然后有一个选择:
    • 如果finally块正常完成,那么try语句突然完成,原因是R
    • 如果finally块因为S而突然完成,则try语句突然完成,原因是S 和原因R被丢弃 )。

return的规范实际上使这个明确:

JLS 14.17返回声明

ReturnStatement:
     return Expression(opt) ;

return语句没有Expression attempts 将控制权转移给包含它的方法或构造函数的调用者。

return语句带有Expression attempts 将控制权转移给包含它的方法的调用者; Expression的值成为方法调用的值。

前面的描述说“ attempts to transfer control”而不仅仅是“传输控制”因为如果方法或构造函数中有try块的try语句包含return语句,那么finally语句的任何try子句将按顺序执行,最内层到最外层,然后将控制权转移给方法或构造函数的调用者。突然完成finally子句可能会破坏由return语句启动的控制权转移。

249
polygenelubricants

除了其他响应之外,重要的是要指出'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();
    }
}
148
Eyal Schneider

我尝试了上面的例子,略有修改 -

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后,再次执行return块的try语句。但这种情况并非如此。它只返回值2。

109
vibhash

这是 凯文的答案 的详细说明。重要的是要知道要返回的表达式在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
95
WoodenKitty

这就是最终块的整体想法。它允许您确保进行清理,否则可能会因为您返回而被忽略,当然。

最后调用 无论发生什么 在try块中( 除非 你调用System.exit(int)或Java虚拟机因其他原因而踢出)。

52
Chris Cooper

考虑这一点的合理方式是:

  1. 放置在finally块中的代码必须执行 无论发生什么 在try块内
  2. 因此,如果try块中的代码尝试返回值或抛出异常,则将项放在“架子上”,直到finally块可以执行
  3. 因为finally块中的代码(根据定义)具有高优先级,所以它可以返回或抛出它喜欢的任何内容。在这种情况下,“架子上”留下的任何东西都将被丢弃。
  4. 唯一的例外是,如果VM在try块期间完全关闭,例如通过'System.exit'
38
Garth Gilmour

除非程序异常终止(如调用System.exit(0)..),否则最后总是执行。所以,你的sysout会打印出来

18
shyam
18
James A. N. Stauffer

除非由于JVM崩溃或对System.exit(0)的调用导致程序异常终止,否则始终执行finally块。

最重要的是,从finally块中返回的任何值都将覆盖在执行finally块之前返回的值,因此在使用try finally时要小心检查所有出口点。

17
user9189

不,并不总是一个例外情况是// 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");
        }
    }
}
17
Rajendra Jadi

最后总是运行这一点,只是因为它在返回后出现在代码中并不意味着它是如何实现的。 Java运行时有责任在退出try块时运行此代码。

例如,如果您有以下内容:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

运行时将生成如下内容:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

如果抛出未捕获的异常,则finally块将运行,异常将继续传播。

12
Motti

这是因为您将i的值指定为12,但未将i的值返回给函数。正确的代码如下:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}
10
Wasim

因为除非你调用System.exit()(或线程崩溃),否则将始终调用finally块。

9
Jay Riggs

答案很简单 _是_

输入:

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
9
Meet

是的,它会被调用。这就是拥有finally关键字的重点。如果跳出try/catch块可能只是跳过finally块,那么就像把System.out.println放在try/catch之外一样。

8
Mendelt

是的,最后块总是执行。大多数开发人员使用此块关闭数据库连接,结果集对象,语句对象以及还使用Java hibernate来回滚事务。

8
Gautam Viradiya

简而言之,在官方的Java文档中(点击 这里 ),它被写成 -

如果在执行try或catch代码时JVM退出,则finally块可能无法执行。同样,如果执行try或catch代码的线程被中断或终止,则即使应用程序作为一个整体继续,finally块也可能无法执行。

8
bikz05

是的,它会的。只有它不会出现JVM退出或崩溃的情况

7
abhig

是的,它会的。无论你的try或catch块发生什么,除非调用System.exit()或JVM崩溃。如果块中有任何返回语句,则最终将在该返回语句之前执行。

7
Karthikeyan

考虑以下程序:

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.
    }
}
7
Samim

我试过这个,它是单线程的。

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");
       }
   }
}

主线程将永远处于等待状态,因此最终永远不会被调用,

所以控制台输出不会打印字符串: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:如果最终块将由守护程序线程执行,并且所有其他非守护程序线程在最终被调用之前退出。

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块”没有执行

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
6
dkb

是的,因为 没有控制语句 可以阻止执行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
    }
}
6
Dávid Horváth

finally将执行,这是肯定的。

finally将不会在以下情况下执行:

情况1 :

当您执行System.exit()时。

案例2:

当您的JVM /线程崩溃时。

案例3:

当您的执行在手动之间停止时。

6
Utkash Bhatt

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中的错误
6
Poorna Senani Gamage

如果不处理异常,则在终止程序之前,JVM会执行finally块。只有当程序的正常执行失败意味着由于以下原因终止程序时才会执行它。

  1. 通过导致致命错误导致进程中止。

  2. 由于内存损坏导致程序终止。

  3. 通过调用System.exit()

  4. 如果程序进入无限循环。

6
Vikas Suryawanshi

这在任何语言中都是正确的...最终将始终在return语句之前执行,无论方法体中返回的位置如何。如果不是这样的话,那么finally块就没有多大意义了。

6
Scott Dorman

添加到 @ 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 
6
Pradeep Kumaresan

是的,它写在 这里

如果在执行try或catch代码时JVM退出,则finally块可能无法执行。同样,如果执行try或catch代码的线程被中断或终止,则即使应用程序作为一个整体继续,finally块也可能无法执行。

5
Danail Tsvetanov

除了关于最终替换try块中的return的返回点之外,异常也是如此。抛出异常的finally块将替换try块内抛出的返回或异常。

5
Alex Miller

不总是

Java语言规范描述了try-catch-finally和try-catch块如何在 14.20.2中工作
无处不在,它指定始终执行finally块。但是对于try-catch-finally和try-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不保证在CODE之后执行FIN。 JLS保证如果CODENEXT被执行,则FIN将始终在CODE之后执行_在NEXT之前。

为什么JLS不保证在try块之后总是执行finally块?因为这是不可能的。在完成try块之后但在执行finally块之前,JVM很可能会被中止(终止,崩溃,断电)。 JLS无法避免这种情况。

因此,任何因其正常行为而依赖于最终块的软件都会在其尝试块完成后始终执行。

Try块中的返回值与此问题无关。如果执行在try-catch-finally之后到达代码,则保证finally块在之前执行,有或没有在try块内返回。

5
Jose Antonio Dura Olmos

在几个独特的场景中返回之后将不会调用finally块:如果首先调用System.exit(),或者JVM崩溃。

让我试着用最简单的方式回答你的问题。

规则1 :finally块总是运行(尽管有例外。但是让我们坚持一段时间。)

规则2 :当控制离开try或catch块时,finally块中的语句运行。控制的转移可以由于正常执行,执行break,continue,goto或a而发生。退货声明或异常的传播。

如果返回语句具体(因为它的标题),控件必须离开调用方法,因此调用相应的try-finally结构的finally块。 return语句在finally块之后执行。

如果finally块中还有一个return语句,它肯定会覆盖try块中挂起的一个,因为它清除了调用栈。

你可以在这里找到一个更好的解释: http://msdn.Microsoft.com/en-us/ ....这个概念在所有高级语言中大致相同。

5
Sandip Solanki
  1. 最后Block总是被执行。除非和直到 System.exit() 语句存在(finally块中的第一个语句)。
  2. 如果 system.exit() 是第一个语句,那么finally块将不会被执行并且控制来自finally块。每当System.exit()语句进入finally块时,直到该语句最终执行块,并且当System.exit()出现时,控制力完全从finally块中出来。
5
Avinash Pande

与以下代码相同:

static int f() {
    while (true) {
        try {
            return 1;
        } finally {
            break;
        }
    }
    return 2;
}

f将返回2!

4
dibo

因为无论你遇到什么情况,总是会被调用。你没有异常,它仍然被调用,捕获异常,它仍然被调用

4
vodkhang

是的,它总是会调用,但在一种情况下,当你使用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.
}
4
Akash Manngroliya

最后块总是执行是否处理异常。如果在try块之前发生任何异常,则finally块将不会执行。

4
Rohit Chugh

如果抛出异常,最后运行。如果没有抛出异常,最后运行。如果捕获到异常,则最终运行。如果未捕获异常,则最终运行。

只有当JVM退出时它才会运行。

4
Bhushan

在正常的执行过程中考虑这一点(即没有抛出任何异常):如果方法不是'void',那么它总是显式返回一些东西,但最后总是被执行

4
Gala101

试试这段代码,你会理解 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;
    }
}
4
eric2323223

我对在不同论坛上提供的所有答案感到非常困惑,并决定最终编码和查看。输出是:

即使在try和catch块中有返回,也会执行。

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");
}

输出

尝试

打印我最后

  1. 如果在上面的代码中try和catch块中的System.exit(0)替换了return,并且由于任何原因在它之前发生异常。
4
milton

如果在嵌套的finally块中抛出异常,也可以提前退出。编译器将警告您finally块未正常完成或出现无法访问代码的错误。仅当throw不在条件语句之后或在循环内时,才会显示无法访问代码的错误。

try{
}finally{
   try{
   }finally{
      //if(someCondition) --> no error because of unreachable code
      throw new RunTimeException();
   }
   int a = 5;//unreachable code
}
3
HopefullyHelpful

最后总是在最后调用

当你尝试时,它执行一些代码,如果在try中发生了什么,那么catch将捕获该异常并且你可以打印一些mssg或抛出错误,然后最后执行块。

最后通常在进行清理时使用,例如,如果你在Java中使用扫描仪,你应该关闭扫描仪,因为它会导致其他问题,例如无法打开某些文件

2
Rubin Luitel

即使在try块中放入return语句,finally块也会始终执行。 finally块将在return语句之前执行。

2
Sabrina