一、前言

前些天参加面试的时候有一道题:

public classtest {

public static voidmain(String[] args){try{return;

}finally{

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

}

}

}

以上程序的执行结果是什么?

当时觉得finally块肯定会被执行到的,而这段程序在try块里就已经返回了,所以选了“编译出现错误”这个选项,回来之后验证了一下,结果是输出“finally…”,越发觉得这个问题很有趣。

二、剖析

1. 从字节码分析

为了更好的说明问题,选用一篇博客里的例子[1]进行说明,源码:

public classTest {

@SuppressWarnings("finally")public static finalString test() {

String t= "";try{

t= "try";returnt;

}catch(Exception e) {

t= "catch";returnt;

}finally{

t= "finally";

}

}public static voidmain(String[] args) {

System.out.print(Test.test());

}

}

按照一般的思路,首先程序执行try语句块,把变量t赋值为try,由于没有发现异常,接下来执行finally语句块,把变量t赋值为finally,然后return t,则t的值是finally,最后t的值就是finally,程序结果应该显示finally,但是实际结果为try。为什么会这样,我们不妨先看看这段代码编译出来的class对应的字节码,看虚拟机内部是如何执行的。

我们用javap -verbose Test 来显示目标文件(.class文件)字节码信息。

系统运行环境:win7 64位

jdk信息:java version "1.8.0_05",Java(TM) SE Runtime Environment (build 1.8.0_05-b13),Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

编译出来的字节码部分信息,我们只看test方法,其他的先忽略掉:

public static finaljava.lang.String test();

descriptor: ()Ljava/lang/String;

flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

Code:

stack=1, locals=4, args_size=0

0: ldc #2 //String

2: astore_03: ldc #3 //String try

5: astore_06: aload_07: astore_18: ldc #4 //String finally

10: astore_011: aload_112: areturn13: astore_114: ldc #6 //String catch

16: astore_017: aload_018: astore_219: ldc #4 //String finally

21: astore_022: aload_223: areturn24: astore_325: ldc #4 //String finally

27: astore_028: aload_329: athrow

Exception table:

from to target type3 8 13 Class java/lang/Exception3 8 24any13 19 24any

LineNumberTable:

line5: 0line8: 3line9: 6line14: 8line10: 13line11: 14line12: 17line14: 19StackMapTable: number_of_entries= 2frame_type= 255 /*full_frame*/offset_delta= 13locals= [ class java/lang/String ]

stack= [ class java/lang/Exception ]

frame_type= 74 /*same_locals_1_stack_item*/stack= [ class java/lang/Throwable ]

观察Code部分:

第[0-2]行,给第0个变量赋值“”,也就是String t="";

第[3-5]行,也就是执行try语句块 赋值语句 ,也就是 t = "try";

第[6-7]行,重点是第7行,把第t对应的值"try"赋给第1个变量,但是这里面第1个变量并没有定义这个比较奇怪;

第[8-10] 行,对第0个变量进行赋值操作,也就是t="finally";

第[11-12]行,把第1个变量对应的值返回;

通过字节码,我们知道,在return之前,虚拟机会创建一个中间变量,我们暂时可以称为t’,然后把t的值赋值给t’,接下来去执行finally块的内容,最后返回t’,所以即使在finally块里修改了t,但是return返回的是t’,所以最后输出的是t’的内容,如下图所示:

2. 如果try和finally块里面修改的是可变的对象

classmyObject{private int value = 0;public myObject(intvalue){this.value =value;

}public void setValue(intvalue){this.value =value;

}public voidprint(){

System.out.println("obj:" + this + ",value:" +value);

}

}public classTest {

@SuppressWarnings("finally")public static finalmyObject test() {

myObject myObj= null;try{

myObj= new myObject(1);

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

myObj.print();returnmyObj;

}catch(Exception e) {

myObj.setValue(2);returnmyObj;

}finally{

myObj.setValue(3);

}

}public static voidmain(String[] args) {

myObject newObj=test();

System.out.println("after return");

newObj.print();

}

}

输出:

in tryblock

obj:myObject@15db9742,value:1afterreturnobj:myObject@15db9742,value:3

在这个例子中,即使myObj会赋值给myObj’,然而他们都指向同一个对象,因此在finally块中对这个对象的修改当然反映到myObj’中。

3. try和finally块中都有return语句

还是用String的例子,不过finally语句增加了return语句

public classTest {

@SuppressWarnings("finally")public static finalString test() {

String t= "";try{

t= "try";returnt;

}catch(Exception e) {

t= "catch";returnt;

}finally{

t= "finally";

returnt;

}

}public static voidmain(String[] args) {

System.out.print(Test.test());

}

}

最后输出:

finally

可见这种情况,执行完finally之后就直接返回了。

4. catch语句被执行的情况并且有return语句

public classTest {

@SuppressWarnings("finally")public static finalString test() {

String t= "";try{

t= "try";

Integer.parseInt(null);returnt;

}catch(Exception e) {

t= "catch";returnt;

}finally{

t= "finally";

}

}public static voidmain(String[] args) {

System.out.print(Test.test());

}

}

最后输出:

catch

可见try和catch语句里都存在使用中间变量的情况。

5. catch块中抛出异常的情况

public classTest {

@SuppressWarnings("finally")public static finalString test() {

String t= "";try{

t= "try";

Integer.parseInt(null);returnt;

}catch(Exception e) {

t= "catch";

Integer.parseInt(null);returnt;

}finally{

t= "finally";

}

}public static voidmain(String[] args) {

System.out.print(Test.test());

}

}

输出:

Exception in thread "main" java.lang.NumberFormatException: nullat java.lang.Integer.parseInt(Integer.java:542)

at java.lang.Integer.parseInt(Integer.java:615)

at Test.test(Test.java:12)

at Test.main(Test.java:20)

执行过程大概是,执行try块,Integer.parseInt(null)语句抛出异常,进入catch语句,然后又抛出异常,然后执行finally块,对t进行赋值,因为finally没有返回,所以执行完之后,catch把异常抛出。

如果finally块中有return语句呢,在finally块最后加多一条语句”return t;”,最后输出:

finally

6. finally块中抛出异常

public classTest {

@SuppressWarnings("finally")public static finalString test() {

String t= "";try{

t= "try";returnt;

}catch(Exception e) {

t= "catch";returnt;

}finally{

t= "finally";

String.valueOf(null);returnt;

}

}public static voidmain(String[] args) {

System.out.print(Test.test());

}

}

输出:

Exception in thread "main" java.lang.NullPointerException[Finished in 1.1s with exit code 1]

at java.lang.String.(String.java:166)

at java.lang.String.valueOf(String.java:2993)

at Test.test(Test.java:14)

at Test.main(Test.java:20)

可见执行到finally块,产生异常之后会终止当前的其他操作,向上抛出异常。

三、小结

try,catch,finally语句中,如果在try/catch块中存在return语句,finally块没有return语句,try/catch块会产生一个临时变量(t’)存储return 语句中的变量(t),如果这个变量类型是值类型或者不可变对象,则在finally块中对变量t的修改不会影响到try/catch中返回的结果;如果是可变对象类型,则结果会影响;

如果finally块中有return语句,则try和catch中的return语句都会忽略;

如果finally块中抛出异常,则停止try…catch…finally中的其他操作,直接向上抛出异常。

四、参考

try catch语句 java_深入剖析java的try…catch…finally语句相关推荐

  1. java中的异常处理代码,java_深入剖析Java中的各种异常处理方式,1. 调试追踪代码:public s - phpStudy...

    深入剖析Java中的各种异常处理方式 1. 调试追踪代码: public static void enterTryMethod() { System.out.println("enter a ...

  2. java批量执行查询sql语句_如何从Java执行多个SQL语句

    小编典典 您可以使用以下示例实现addBatch和executeBatch命令同时执行多个 SQL 命令. 批处理允许您将相关的SQL语句分组为一个批处理,并通过一次调用将其提交给数据库.参考 当您一 ...

  3. Java里try catch的简单用法

    Java里try catch的简单用法: Java里try catch的简单用法: 1.try+catch 程序的流程是:运行到try块中,如果有异常抛出,则转到catch块去处理.然后执行catch ...

  4. Java中try catch finally语句中含有return语句的执行情况

    最近刷笔试题遇到的问题,记录下来. 在这里看到了try >但有一点是可以肯定的,finally块中的内容会先于try中的return语句执行,如果finall语句块中也有return语句的话,那 ...

  5. Java Switch语句及性能剖析(转载补充)

    为什么80%的码农都做不了架构师?>>>    Java Switch语句及性能剖析(转载补充) 一.基本用 switch的case语句可以处理int,short,byte,char ...

  6. 视频教程-Java异常原理剖析-Java

    Java异常原理剖析 曾任职于国内知名校企合作单位.超过6年教育行业工作经验.曾在多家线上教育平台讲授过公开课.和VIP课程,擅长讲授JAVASE.JAVAWEB相关的课程.授课氛围清晰自然,讲解内容 ...

  7. python代码转成java_如何实现Java代码转换成python代码

    之前小编告诉大家最初是学Java的,现在转向python,那对于两者之间有什么互通的嘛?其实有,比如可以将Java转成python代码,一起来看下吧. 首先给大家带来一组示例演示. Java代码如下: ...

  8. java try、catch、finally及finally执行顺序详解

    1.为什么要用finally 先看一个没有finally的异常处理try-catch语句: 假设count为要使用到的资源,并且用完要求释放此资源.那么我们可以把释放资源的语句放到try-catch后 ...

  9. Java SE 12扩展Switch语句/表达式完整指南

    本文提供了Java SE 12扩展Switch语句/表达式的完整指南.文章详细介绍了扩展Java switch语句将其用作增强版switch语句或表达式.为帮助理解本文提供了具体案例. 本文要点 现在 ...

最新文章

  1. Java 虚拟机总结给面试的你(中)
  2. 编译nginx时的两个报错
  3. 开源:推荐一个不错的离线IP地址定位库
  4. Python正则表达式re模块简明笔记
  5. 美国农业生产与农产品国际贸易 对话国际农民丰收节贸易会
  6. JZOJ 5184. 【NOIP2017提高组模拟6.29】Gift
  7. C语言实现拓扑排序topological sort算法(附完整源码)
  8. 需求奇葩不可怕,可怕的是变幻无常
  9. Android中文API(128) —— HandlerThread
  10. JQuery提供了丰富的选择器
  11. usb, micro-usb card 损坏, 数据恢复
  12. ppt抽奖vba代码
  13. 分布式数据库NoSQL(五)——MongoDB 之滴滴、摩拜都在用的索引
  14. 奇迹服务器如何修改爆率,奇迹萌新教程系列-奇迹装备是否掉落配置调整
  15. 描写火车站场景_优美段落:描写车站的经典美文摘抄
  16. php开发-如何调用后端服务
  17. 有些人走着走着就散了,有些事想着想着就忘了……
  18. 蓝牙学习之①:调戏小米手环
  19. 人工智能领域6大关键创新,AI可以用在哪些领域?
  20. 富士色彩配方咖啡馆在上海开业;万代南梦宫娱乐加入玩游戏,救地球联盟 | 全球TMT...

热门文章

  1. mysql 减少文件排序_消除更新查询中的mysql文件排序
  2. PlayMaker的特殊事件FINISHED
  3. Swift3.0语言教程使用路径字符串
  4. Arduino可穿戴开发入门教程LilyPad和LilyPad Simple的介绍
  5. Arduino Yun快速入门教程(大学霸内部资料)
  6. base64编码 vba_VB VBA ASP 可通用的基于Base64进行加密和解密的函数
  7. fanuc机器人与plc的通讯_S7-1200PLC与FANUC机器人Profinet通讯方法
  8. 耶鲁大学等机构提出的脑机接口软硬件协同设计,增加脑机的更大潜力
  9. EMD算法之Hilbert-Huang Transform原理详解和案例分析
  10. 科大讯飞2位董事同日辞职,联合创始人胡郁发力造芯