美文网首页
关于 Java 中 finally 语句块的深度辨析

关于 Java 中 finally 语句块的深度辨析

作者: grace_fang | 来源:发表于2019-12-12 19:41 被阅读0次

    问题分析

    1.finally关键字

    首先抛出一个问题,finally 一定执行吗

    很多人都认为finally语句块是肯定要执行的
    答案是否定的。我们先来看下面这个例子

    exp1:

    public class Test { 
    public static void main(String[] args) { 
    System.out.println("return value of test(): " + test());  } 
    public static int test() { 
    int i = 1; 
    //          if(i == 1) 
    //              return 0; 
    System.out.println("the previous statement of try block"); 
    i = i / 0;   
    try { 
       System.out.println("try block"); 
         return i; 
        }finally { 
        System.out.println("finally block"); 
            } 
        } 
    }
    

    执行结果1:

    the previous statement of try block 
    Exception in thread "main" java.lang.ArithmeticException: / by zero 
    at com.bj.charlie.Test.test(Test.java:15) 
    at com.bj.charlie.Test.main(Test.java:6)
    

    如果去掉上面的注释

    return value of test(): 0
    

    在以上两种情况下,finally 语句块都没有执行,说明什么问题呢?只有与 finally 相对应的 try 语句块得到执行的情况下,finally 语句块才会执行。以上两种情况,都是在 try 语句块之前返回(return)或者抛出异常,所以 try 对应的 finally 语句块没有执行。

    那好,即使与 finally 相对应的 try 语句块得到执行的情况下,finally 语句块一定会执行吗?不好意思,这次可能又让大家失望了,答案仍然是否定的。请看下面这个例子(exp 2)

     */
    public class Test {
        public static void main(String[] args) {
            System.out.println("return value of test(): " + test());
        }
    
        public static int test() {
            int i = 1;
    
            try {
                System.out.println("try block");
                System.exit(0);
                return i;
            }finally {
                System.out.println("finally block");
            }
        }
    }
    

    执行结果:
    [图片上传失败...(image-d2f110-1576150830402)]

    finally 语句块还是没有执行,为什么呢?因为我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。那有人说了,在一般的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的。OK !没有问题,我们不调用 System.exit(0) 这个方法,那么 finally 语句块就一定会执行吗?

    再一次让大家失望了,答案还是否定的。当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。

    不管 try 语句块正常结束还是异常结束,finally 语句块是保证要执行的。如果 try 语句块正常结束,那么在 try 语句块中的语句都执行完之后,再执行 finally 语句块。如果 try 中有控制转移语句(return、break、continue)呢?那 finally 语句块是在控制转移语句之前执行,还是之后执行呢?似乎从上面的描述中我们还看不出任何端倪,不要着急,后面的讲解中我们会分析这个问题。如果 try 语句块异常结束,应该先去相应的 catch 块做异常处理,然后执行 finally 语句块。同样的问题,如果 catch 语句块中包含控制转移语句呢? finally 语句块是在这些控制转移语句之前,还是之后执行呢?我们也会在后续讨论中提到。

    finally 语句示例说明

    下面,我们先来看一个简单的例子

    public class Test { 
    public static void main(String[] args) {  
    try {  
    System.out.println("try block");  
     
    return ;  
    } finally {  
    System.out.println("finally block");  
            }  
        }  
    }
    

    执行结果:


    image.png

    说明 finally 语句块在 try 语句块中的 return 语句之前执行。我们再来看另一个例子

    public class Test3 {
        public static void main(String[] args) {
            System.out.println("reture value of test() : " + test());
        }
    
        public static int test(){
            int i = 1;
    
            try {
                System.out.println("try block");
                i = 1 / 0;
                return i;
            }catch (Exception e){
                System.out.println("exception block");
                return 2;
            }finally {
                System.out.println("finally block");
            }
        }
    }
    

    执行结果是


    image.png

    这说明了finally语句块在catch语句中的return 语句之前执行

    我们可以看出,其实 finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。更加一般的说法是,finally 语句块应该是在控制转移语句之前执行,控制转移语句除了 return 外,还有 break 和 continue。另外,throw 语句也属于控制转移语句。虽然 return、throw、break 和 continue 都是控制转移语句,但是它们之间是有区别的。其中 return 和 throw 把程序控制权转交给它们的调用者(invoker),而 break 和 continue 的控制权是在当前方法内转移。请大家先记住它们的区别,在后续的分析中我们还会谈到。

    public class Test5 {
        public static void main(String[] args) {
            System.out.println("return value of getValue(): " + getValue());
        }
    
        public static int getValue() {
            try {
                return 0;
            } finally {
                return 1;
            }
        }
    }
    

    执行结果:


    image.png
    public class Test6 {
        public static void main(String[] args) {
            System.out.println("return value of getValue(): " + getValue());
        }
    
        public static int getValue() {
            int i = 1;
            try {
                return i;
            } finally {
                i++;
            }
        }
    }
    
    image.png

    利用我们上面分析得出的结论:finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。 由此,可以轻松的理解清单 5 的执行结果是 1。因为 finally 中的 return 1;语句要在 try 中的 return 0;语句之前执行,那么 finally 中的 return 1;语句执行后,把程序的控制权转交给了它的调用者 main()函数,并且返回值为 1。那为什么清单 6 的返回值不是 2,而是 1 呢?按照清单 5 的分析逻辑,finally 中的 i++;语句应该在 try 中的 return i;之前执行啊? i 的初始值为 1,那么执行 i++;之后为 2,再执行 return i;那不就应该是 2 吗?怎么变成 1 了呢?

    finally: 是异常处理的一部分,用于释放资源,
    一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了

    相关文章

      网友评论

          本文标题:关于 Java 中 finally 语句块的深度辨析

          本文链接:https://www.haomeiwen.com/subject/ltatnctx.html