美文网首页
关于 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