当前位置 博文首页 > 有图有真相:Java异常机制--try catch finally 执行顺序详解

    有图有真相:Java异常机制--try catch finally 执行顺序详解

    作者:[db:作者] 时间:2021-07-19 16:27

    引言

    关于try catch finally 执行顺序的笔试面试题目非常的多,我曾经在牛客网刷题的时候不止一次的碰到过,而且不止一次的做错过,这里面需要涉及的细节如果不弄清楚每次做题就会产生似是而非的感觉。这次查阅了很多相关资料,关于try catch finally 执行顺序各方面基本都讲到了。不足之处欢迎指出。

    try catch finally 执行顺序

    仅仅在下面4中情况下不会执行finally语句 :

    ①.如果在try 或catch语句中执行了System.exit(0)。

    ②.在执行finally之前jvm崩溃了。

    ③.try语句中执行死循环。

    ④.电源断电。


    除了以上的四种情况外,finally语句都会执行,finally语句执行时会有以下原则。

    ①、不管有没有出现异常,finally块中代码都会执行;

      public void demo1(){
            try {
    
              System.out.println(result);
    
            } catch (Exception e) {                     
                System.out.println(e.getMessage());
            }
             finally {            
                    System.out.println("finally trumps. ");
                }
    //输出结果为:
    result
    finally trumps .

    上面代码可知如果未出现异常是顺序执行try和finally代码块。

    ②、当try和catch中有return时,finally仍然会执行;

    public static int demo2() {
            try {
                return 0;
            }
            finally {
                System.out.println("finally trumps return.");
            }
        }
    //输出结果
    
    finally trumps return.
    0   

    当finally里面没有return语句是,执行try 和finally语句之后最后再执行return。
    ③、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;

    public static int demo3()
        {
             int i = 0;
                try {
                    i = 2;
                    return i;
                } finally {
                    i = 12;
                    System.out.println("finally trumps return.");
                }       
        }
    //输出结果
        finally trumps return.
        2

    此处中finally中对i赋值12但是demo3的返回值仍然是2,也就是在finally中对i赋值并未改变i的返回值,这里需要详细的讲一下,此处涉及到了jvm机制。先给出上面代码的字节码然后给出图解:

    public static demo3()I
        TRYCATCHBLOCK L0 L1 L2 
       L3
        LINENUMBER 12 L3
        ICONST_0
        ISTORE 0
       L0
        LINENUMBER 14 L0
        ICONST_2
        ISTORE 0
       L4
        LINENUMBER 15 L4
        ILOAD 0
        ISTORE 2
       L1
        LINENUMBER 17 L1
        BIPUSH 12
        ISTORE 0
       L5
        LINENUMBER 18 L5
        GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
        LDC "finally trumps return."
        INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
       L6
        LINENUMBER 15 L6
        ILOAD 2
        IRETURN
       L2
        LINENUMBER 16 L2
       FRAME FULL [I] [java/lang/Throwable]
        ASTORE 1
       L7
        LINENUMBER 17 L7
        BIPUSH 12
        ISTORE 0
       L8
        LINENUMBER 18 L8
        GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
        LDC "finally trumps return."
        INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
       L9
        LINENUMBER 19 L9
        ALOAD 1
        ATHROW

    上面的字节码比较长,下面简要的讲一下,其实我在http://blog.csdn.net/u013309870/article/details/72935274这篇文章中详细的讲过方法中代码的执行过程,
    这里写图片描述
    在variable内存中有两个变量区域一个是用来存放i的值,对应最上面的那个,另一个用于存放返回值。在上面代码执行到i = 2; return i;先对i赋值2,然后执行return语句此时并不是将结果返回,而是将i=2的值保存到返回值变量区域,在执行完i=12时,再返回variable中返回值地址变量区域的2。

    ④、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

        public static int demo4() {
            int i = 0;
            try {
                return i;
            } finally {
                i = 12;
                System.out.println("finally trumps return.");
                return i;
            }
        }
        //输出结果
        finally trumps return.
        12  

    上面为什么会返回12呢?因为在程序还未执行try中的return语句时就先执行了finally里面的return语句所以返回结果为12。

    经典面试题

    下面题目输出什么?

    
        public static int demo5() {
            try {
                return printX();
            }
            finally {
                System.out.println("finally trumps return... sort of");
            }
        }
        public static int printX() {
    
            System.out.println("X");
            return 0;
        }
    

    输出结果:

    X
    finally trumps return... sort of
    0

    上面这道题目含金量很高,程序顺序执行时先执行printX()函数,此时得到返回值0并且将0保存到variable中对应的用于保存返回值的区域,此时程序在执行finally语句因为finally语句中没有return语句,所以程序将返回值区域的0返回给上一级函数。

    参考文献

    https://stackoverflow.com/questions/65035/does-finally-always-execute-in-java
    http://blog.csdn.net/u013309870/article/details/72935274
    深入理解Java虚拟机(高清第二版)JVM高级特性与最佳实践

    cs