关于返回:最后一个块总是在Java中执行吗?

关于返回:最后一个块总是在Java中执行吗?

Does a finally block always get executed in Java?

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

1
2
3
4
5
6
7
8
9
10
try {  
    something();  
    return success;  
}  
catch (Exception e) {  
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}

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

唯一不被调用的时间是:

  • 如果你调用System.exit()
  • 如果JVM首先崩溃
  • 如果jvm在trycatch块中达到无限循环(或其他一些不可中断、不终止的语句)
  • 如果操作系统强制终止JVM进程,例如,UNIX上的kill -9
  • 如果主机系统死机,例如电源故障、硬件错误、操作系统死机等
  • 如果finally块将由守护进程线程执行,并且所有其他非守护进程线程在调用finally之前退出

  • 示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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.");
        }
    }

    输出:

    1
    2
    finally trumps return.
    0

    另外,尽管这是一个糟糕的实践,但是如果finally块中有一个返回语句,它将胜过常规块中的任何其他返回。也就是说,下面的块将返回false:

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

    与从finally块抛出异常相同。


    下面是Java语言规范的官方词汇。

    2.20.2.Try Finally和Try Catch Finally的执行

    A try statement with a finally block is executed by first executing the try block. Then there is a choice:

    • If execution of the try block completes normally, [...]
    • If execution of the try block completes abruptly because of a throw of a value V, [...]
    • If execution of the try block completes abruptly for any other reason R, then the finally block is executed. Then there is a choice:

      • If the finally block completes normally, then the try statement completes abruptly for reason R.
      • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

    return的规范实际上明确了这一点:

    JLS 14.17退货声明

    1
    2
    ReturnStatement:
         return Expression(opt) ;

    A return statement with no Expression attempts to transfer control to the invoker of the method or constructor that contains it.

    A return statement with an Expression attempts to transfer control to the invoker of the method that contains it; the value of the Expression becomes the value of the method invocation.

    The preceding descriptions say"attempts to transfer control" rather than just"transfers control" because if there are any try statements within the method or constructor whose try blocks contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.


    除了其他响应之外,重要的是指出"finally"有权重写try..catch块的任何异常/返回值。例如,以下代码返回12:

    1
    2
    3
    4
    5
    6
    7
    8
    public static int getMonthsInYear() {
        try {
            return 10;
        }
        finally {
            return 12;
        }
    }

    同样,以下方法不会引发异常:

    1
    2
    3
    4
    5
    6
    7
    8
    public static int getMonthsInYear() {
        try {
            throw new RuntimeException();
        }
        finally {
            return 12;
        }
    }

    当以下方法确实抛出它时:

    1
    2
    3
    4
    5
    6
    7
    8
    public static int getMonthsInYear() {
        try {
            return 12;          
        }
        finally {
            throw new RuntimeException();
        }
    }

    我对上面的例子做了一些修改-

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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.");
        }
    }

    以上代码输出:

    finally trumps return.
    2

    这是因为当执行return i;时,i的值为2。在此之后,执行finally块,其中12分配给i,然后执行System.out输出。

    执行finally块后,try块返回2,而不是返回12,因为此返回语句不会再次执行。

    如果您将在Eclipse中调试此代码,那么在执行finally块的System.out之后,您会感觉到try块的return语句再次执行。但事实并非如此。它只返回值2。


    下面是对凯文答案的详细阐述。重要的是要知道要返回的表达式是在finally之前计算的,即使它是在之后返回的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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");
        }
    }

    输出:

    1
    2
    3
    X
    finally trumps return... sort of
    0

    这就是最后一个街区的全部想法。它让你确保你做清理,否则可能会被跳过,因为你回来,当然,还有其他事情。

    最终调用不管尝试中发生什么(除非您调用EDCOX1 1)或Java虚拟机出于其他原因开机。


    一个合乎逻辑的思考方法是:

  • 放置在finally块中的代码必须执行try块中发生的任何操作。
  • 因此,如果try块中的代码试图返回值或引发异常,则该项将被放置在"工具架"上,直到最后一个块可以执行为止。
  • 因为finally块中的代码(根据定义)具有很高的优先级,所以它可以返回或抛出它喜欢的任何代码。在这种情况下,"搁在架子上"的任何东西都会被丢弃。
  • 唯一的例外是,如果VM在try块期间完全关闭,例如"system.exit"

  • 最后总是执行,除非有异常程序终止(如调用System.Exit(0))。所以你的系统会被打印出来


    最后一次返回将丢弃任何异常。http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


    最后一个块总是被执行,除非有异常的程序终止,要么是由于JVM崩溃,要么是由于调用System.exit(0)

    除此之外,finally块中返回的任何值都将覆盖finally块执行之前返回的值,因此在使用try finally时,请注意检查所有退出点。


    不,不一定只有一个例外//退出系统(0);在finally块阻止最终执行之前。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      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运行时有责任在退出EDCOX1"1"块时运行此代码。

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

    1
    2
    3
    4
    5
    6
    7
    8
    int foo() {
        try {
            return 42;
        }
        finally {
            System.out.println("done");
        }
    }

    运行时将生成如下内容:

    1
    2
    3
    4
    5
    int foo() {
        int ret = 42;
        System.out.println("done");
        return 42;
    }

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static int test() {
        int i = 0;
        try {
            return i;
        } finally {
            i = 12;
            System.out.println("finally trumps return.");
            return i;
        }
    }

    Answer is simple YES.

    输入:

    1
    2
    3
    4
    5
    6
    7
    8
    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");
    }

    输出:

    1
    2
    catch
    finally

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


    是的,它会被呼叫的。这就是使用finally关键字的关键所在。如果从try/catch块中跳出可以跳过finally块,则与将system.out.println放在try/catch之外相同。


    是的,finally块始终执行。大多数开发人员使用这个块来关闭数据库连接、结果集对象、语句对象,并使用到Java Hibernate中回滚事务。


    简而言之,在官方Java文档中(点击这里),

    If the JVM exits while the try or catch code is being executed, then
    the finally block may not execute. Likewise, if the thread executing
    the try or catch code is interrupted or killed, the finally block may
    not execute even though the application as a whole continues.


    考虑以下程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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");
            }
        }
    }

    对于Java1.8162,上面的代码块给出以下输出:

    1
    2
    3
    4
    5
    -abc-
    ---AGAIN---
    -abc-xyz-abc-
    ---PRINT THE RESULT---
    -abc-xyz-abc-xyz

    这意味着使用finally释放对象是一个很好的实践,如以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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.
        }
    }

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


    是的。唯一不会发生的情况是JVM退出或崩溃


    是的,因为没有控制语句可以阻止执行finally

    下面是一个参考示例,其中将执行所有代码块:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    | 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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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的引用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
        }
    }


    如果不处理异常,在终止程序之前,JVM将执行finally块。只有当程序的正常执行因以下原因导致Mean终止程序时,它才会执行。

  • 导致导致进程中止的致命错误。

  • 由于内存损坏而终止程序。

  • 通过调用System.Exit()。

  • 如果程序进入无限循环。


  • trycatchfinally是使用异常处理案例的关键词。正常解释

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
    }

    最后一个块阻止执行…

    • 当你打电话给System.exit(0);
    • 如果JVM退出。
    • JVM中的错误

    这在任何语言中都是正确的…finally总是在返回语句之前执行,不管返回在方法体中的什么位置。如果不是这样的话,最后一块就没有什么意义了。


    finally将执行,这是肯定的。

    在下列情况下,finally将不执行:

    案例1:

    在执行System.exit()时。

    案例2:

    当您的JVM/线程崩溃时。

    案例3:

    当手动停止执行时。


    我试过了,它是单螺纹的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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达成一致,上面的示例是这里提到的第三个案例之一:

    在下面的代码中添加更多这样的无限循环可能性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 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首先崩溃

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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);
        }
    }

    裁判:你怎么让一个合资企业破产?

    案例6:如果finally块将由守护进程线程执行,并且所有其他非守护进程线程在finally被调用之前退出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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",这意味着"daemon thread"中的"finally block"没有执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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的答案中,因为没有其他答案可以解释在如下可变对象的情况下会发生什么。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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");
        }
    }

    意志输出

    1
    sbupdated

    是的,写在这里

    If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.


    除了关于在最后替换try块中的返回的要点外,异常也是如此。抛出异常的finally块将替换从try块中抛出的返回或异常。


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

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

    规则1:finally块始终运行(尽管也有例外。但我们还是坚持一段时间吧。)

    规则2:当控件离开try或catch块时,finally块中的语句将运行。控件的传输可能是由于正常执行、执行break、continue、goto或return语句,或异常的产生。

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

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

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


  • finally块始终被执行。除非和直到存在system.exit()语句(finally块中的第一个语句)。
  • 如果system.exit()是第一条语句,那么finally块将不会被执行,控制将从finally块中出来。每当system.exit()语句进入finally块,直到该语句最终执行块,当system.exit()出现时,控制力就会完全从finally块中出来。

  • 并不总是

    Java语言规范描述了如何尝试catch catch并尝试catch块在1420.2中工作。在任何地方,它都不会指定始终执行finally块。但是,对于try catch finally和try finally块完成的所有情况,它都指定必须在完成之前最终执行。

    1
    2
    3
    4
    5
    6
    7
    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。JLS保证,如果执行代码和下一个代码,那么fin将始终在代码之后和下一个代码之前执行。

    为什么jls不能保证在try块之后始终执行finally块?因为这是不可能的。在完成try块之后,但在执行finally块之前,JVM不太可能会中止(终止、崩溃、关机)。JLS对此无能为力。

    因此,对于其正确行为依赖于finally块的任何软件,总是在其try块完成后执行。

    try块中的返回与此问题无关。如果在try catch finally之后执行到代码,则可以保证finally块之前已经执行过,无论在try块内是否返回。


    因为在任何情况下都会调用final。您没有异常,它仍然被调用,捕获异常,它仍然被调用


    是的,它将始终调用,但在一种情况下,使用System.Exit()时它不会调用

    1
    2
    3
    4
    5
    6
    7
    8
    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.
    }

    如果抛出异常,则最终运行。如果未引发异常,则最终运行。如果捕获到异常,则最终运行。如果未捕获异常,则最终运行。

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


    试试这段代码,你会明白代码在finally块被执行后返回语句。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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;
        }
    }

    与以下代码相同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    static int f() {
        while (true) {
            try {
                return 1;
            } finally {
                break;
            }
        }
        return 2;
    }

    F将返回2!


    无论异常处理与否,finally块始终执行。如果在try block之前发生异常,finally块将不会执行。


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

    即使在try and catch块中有返回,也将执行finally。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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");
    }

    产量

    try

    Print me FINALLY

  • 如果上述代码的try-and-catch块中的返回被System.exit(0)替换,并且由于任何原因在它之前发生异常。

  • 在正常的执行过程中考虑这一点(即不引发任何异常):如果方法不是"void",则它总是显式返回某些内容,但最终总是得到执行


    如果在嵌套的finally块中引发异常,finally也可以提前退出。编译器会警告您,finally块没有正常完成,或者给出一个错误,说明您有无法访问的代码。只有当throw不在条件语句后面或循环内时,才会显示不可访问代码的错误。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try{
    }finally{
       try{
       }finally{
          //if(someCondition) --> no error because of unreachable code
          throw new RunTimeException();
       }
       int a = 5;//unreachable code
    }


    最后总是在末尾调用

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

    最后,通常在清理时使用,例如,如果使用Java中的扫描器,则可能会关闭扫描仪,因为它会导致其他问题,例如无法打开某些文件。


    finally块始终被执行,即使您在try块中放置了一个RETURN语句。finally块将在返回语句之前执行。


    推荐阅读