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

清单 2.

清单 2 的执行结果为:

清单 2 说明了 finally 语句块在 catch 语句块中的 return 语句之前执行。

从上面的清单 1 和清单 2,我们可以看出,其实 finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。更加一般的说法是,finally 语句块应该是在控制转移语句之前执行,控制转移语句除了 return 外,还有 break 和 continue。另外,throw 语句也属于控制转移语句。虽然 return、throw、break 和 continue 都是控制转移语句,但是它们之间是有区别的。其中 return 和 throw 把程序控制权转交给它们的调用者(invoker),而 break 和 continue 的控制权是在当前方法内转移。

清单 3.

清单 3 的执行结果:

清单 4.

清单 4 的执行结果:

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

关于 Java 虚拟机是如何编译 finally 语句块的问题,有兴趣的读者可以参考《 The JavaTM Virtual Machine Specification, Second Edition 》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译 finally 语句块。实际上,Java 虚拟机会把 finally 语句块作为 subroutine(对于这个 subroutine 不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解。)直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。请注意,前文中我们曾经提到过 return、throw 和 break、continue 的区别,对于这条规则(保留返回值),只适用于 return 和 throw 语句,不适用于 break 和 continue 语句,因为它们根本就没有返回值。

是不是不太好理解,那我们就用具体的例子来做形象的说明吧!

为了能够解释清单 4 的执行结果,我们来分析一下清单 4的字节码(byte-code):

对于 Test()构造方法与 main()方法,在这里,我们不做过多解释。让我们来分析一下 getValue()方法的执行。在这之前,先让我把 getValue()中用到的虚拟机指令解释一下,以便读者能够正确的理解该函数的执行。

有了以上的 Java 虚拟机指令,我们来分析一下其执行顺序:分为正常执行(没有 exception)和异常执行(有 exception)两种情况。我们先来看一下正常执行的情况,如图 1 所示:

图 1. getValue()函数正常执行的情况

由上图,我们可以清晰的看出,在 finally 语句块(iinc 0, 1)执行之前,getValue()方法保存了其返回值(1)到本地表量表中 1 的位置,完成这个任务的指令是 istore_1;然后执行 finally 语句块(iinc 0, 1),finally 语句块把位于 0 这个位置的本地变量表中的值加 1,变成 2;待 finally 语句块执行完毕之后,把本地表量表中 1 的位置上值恢复到操作数栈(iload_1),最后执行 ireturn 指令把当前操作数栈中的值(1)返回给其调用者(main)。这就是为什么清单 6 的执行结果是 1,而不是 2 的原因。

再让我们来看看异常执行的情况。是不是有人会问,你的清单 6 中都没有 catch 语句,哪来的异常处理呢?我觉得这是一个好问题,其实,即使没有 catch 语句,Java 编译器编译出的字节码中还是有默认的异常处理的,别忘了,除了需要捕获的异常,还可能有不需捕获的异常(如:RunTimeException 和 Error)。

从 getValue()方法的字节码中,我们可以看到它的异常处理表(exception table), 如下:

Exception table:

from to target type

2 4 9 any

它的意思是说:如果从 2 到 4 这段指令出现异常,则由从 9 开始的指令来处理。

图 2. getValue()函数异常执行的情况

先说明一点,上图中的 exception 其实应该是 exception 对象的引用,为了方便说明,我直接把它写成 exception 了。

由上图(图 2)可知,当从 2 到 4 这段指令出现异常时,将会产生一个 exception 对象,并且把它压入当前操作数栈的栈顶。接下来是 astore_2 这条指令,它负责把 exception 对象保存到本地变量表中 2 的位置,然后执行 finally 语句块,待 finally 语句块执行完毕后,再由 aload_2 这条指令把预先存储的 exception 对象恢复到操作数栈中,最后由 athrow 指令将其返回给该方法的调用者(main)。

清单 5.

清单 5 的执行结果:

清单 6.

清单 6 的执行结果:

清单 5 和清单 6 应该还比较简单吧!利用我们上面讲解的知识,很容易分析出其结果。让我们再来看一个稍微复杂一点的例子 – 清单 7。我建议大家最好先不要看执行结果,运用学过的知识来分析一下,看是否能推断出正确的结果。

清单 7.

清单 7 的结果:

你分析对了吗?其实这个案例也不算很难,return test1();这条语句等同于 :

try中如果return的是一个引用而不是基本数据类型, 那么在finally中改变这个引用,就会影响return的返回值

public class TryCatchFinally {

class Test{

int a = 20;

int b = 10;

public void setA(int aa){

this.a=aa;

}

public void setB(int bb){

this.b=bb;

}

public int getA(){

return this.a;

}

public int getB(){

return this.b;

}

}

public static void main(String[] args){

TryCatchFinally tryCatchFinally = new TryCatchFinally();

Test t = tryCatchFinally.testFinally();

System.out.println("引用类型的finally测试: " + t.getA() + " ; " + t.getB());

System.out.println("========================");

int sum = tryCatchFinally.testFinally1();

System.out.println("基本数据类型的finally测试: " + sum);

}

public Test testFinally(){

Test test = new Test();

try{

System.out.println("try");

return test;

}catch (Exception e){

System.out.println("catch");

}finally {

System.out.println("finally");

test.setA(100);

test.setB(200);

}

return test;

}

public int testFinally1(){

int s1 = 2;

int s2 = 3;

try{

System.out.println("try");

return s1+s2;

}catch (Exception e){

System.out.println("catch");

}finally {

System.out.println("finally");

s1 = 100;

s2 = 100;

}

return s1+s2;

}

}

输出结果:

try

finally

引用类型的finally测试: 100 ; 200

========================

try

finally

基本数据类型的finally测试: 5

finally代码块不一定被执行, 以下两种情况finally代码块不被执行:

1).当程序进入try语句块之前就出现异常, 此时会直接结束

例如:

int i = 5/0;//此处出现异常,根本就不会进入try语句块

try{

System.out.println("hello");

}catch(Exception e){

System.out.println("aaa");

}finally{

System.out.println("finally");

}

2).当程序在try块中强制退出时也不会执行finally块中的代码.

例如:

try{

System.out.println("try block");

System.exit(0);//强制退出系统,导致finally代码块不被执行

}catch(Exception e){

System.out.println("catch block");

}finally{

System.out.println("finally block");

}

java中finally用法_java中的finally用法总结相关推荐

  1. java this用法_java中this用法小结

    Java关键字this只能用于方法方法体内.当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是this.因此,this只能在类中的非静态方法中使用,静态 ...

  2. java中的this的用法_java中this的用法

    This,英语单词,发音:[英][ðɪs][美][ðɪs].常翻译为:这,这么.java中this的用法有哪些呢?本文是学习啦小编整理java中this的用法的资料,仅供参考. java中this的用 ...

  3. java static用法_Java中static关键字的作用和用法详细介绍

    static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static ...

  4. java .this的用法_JAVA中this用法小结

    Java中的this随处可见,用法也多,现在整理有几点: 1. this是指当前对象自己. 当在一个类中要明确指出使用对象自己的的变量或函数时就应该加上this引用.如下面这个例子中: 1 publi ...

  5. java中的多态_Java中的多态

    多态与HoFs 朋友们好久不见啊,最近笔者同时在写脚本型语言--JavaScript,和工业级的面向对象语言--Java. 在写代码的同时呢,也会思考这些语言的不同.今天就拿 Java 中的多态,来谈 ...

  6. java和equals区别_JAVA中==与equals的区别

    equals如果没有被重写的话,和==的作用是一样的,都是判断两个对象引用是否指向同一个地址.一般重写了equals()方法就表示比较它们"实际意义上相等",比较的是内容,而不是引 ...

  7. java 中特殊的_Java中一些特殊关键字

    transient 被transient修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null. ins ...

  8. java中的枚举_Java中的枚举

    java中的枚举 Enum was introduced in Java 1.5 as a new type whose fields consists of a fixed set of const ...

  9. java多线程 线程安全_Java中的线程安全

    java多线程 线程安全 Thread Safety in Java is a very important topic. Java provides multi-threaded environme ...

  10. java 异常处理发生异常_Java中的异常处理

    java 异常处理发生异常 Exception Handling in Java is a very interesting topic. Exception is an error event th ...

最新文章

  1. java数组中最小的k个元素_java – 在数组中找到k个最小整数
  2. intellij idea 1314 插件推荐及快速上手建议 (已更新!)
  3. 【转】Mybatis传多个参数(三种解决方案)
  4. 在HTML中使用CSS美化网页的三种方法
  5. oracle管理员是sys吗,Oracle管理员sys,system登录无权限的坑
  6. 【算法知识】详解希尔排序算法
  7. 不是mysql常用类型的是什么_下列选项中不是MySQL中常用数据类型的是()
  8. boost::make_recursive_variant相关的测试程序
  9. Java面向对象概述
  10. Ida双开定位android so文件
  11. Spring三种对象创建方式
  12. PWN-PRACTICE-BUUCTF-29
  13. js学习笔记 chapter5 引用类型
  14. 计算机it dt ct基础知识,ot是什么意思(什么是CT,IT,DT,OT)
  15. Unity性能优化之Resources System
  16. SQL-10-14 4-4 查询具有最高价格的机器的型号,机器包括PC、Laptop、Printer (10分)
  17. LQ-630K打印发票右边打不全?
  18. 防坑指南 | 转行产品经理你需要了解什么?
  19. 计算机技能大赛简讯内,科技节现场类比赛简讯
  20. Deep-Person: Learning Discriminative Deep Features for Person Re-Identification

热门文章

  1. 盘点:12种动画制作工具让游戏角色栩栩如生
  2. Mac卸载 npm 和 n
  3. Mybatis-plus使用SqlInjector注入SQl
  4. 工业机器人编程语言c,非常实用的工业机器人编程语言有哪些?这些编程好用吗?...
  5. Dan Rayburn: 流媒体服务所做的任何事情都必须有其商业价值
  6. linux屏幕分辨率文件,linux下屏幕分辨率的手工调整
  7. Swift编译器Crash—Segmentation fault解决方案
  8. 二分查找(折半查找)算法
  9. 2018年的几点建议
  10. 语音识别芯片模块LD3320到底甩了新唐ISD9160几条街