作者:娇Rex_630 | 来源:互联网 | 2023-07-10 01:57
文章是对两位博主的总结,提炼,原文如下链接:从字节码看trycatchfinally的return如何执行Java中trycatchfinally语句中含有return语句的执
文章是对两位博主的总结,提炼,原文如下链接:
从字节码看try catch finally的return如何执行
Java中try catch finally语句中含有return语句的执行情况(总结版)
测试代码很简单,如下:
Test.java
public class Test {public int get() {try{return 0;} catch (Exception e) {e.printStackTrace();return 1;} finally {return 2;}}
}
执行$ javap -verbose Test.class
$ javap -verbose Test.class
Classfile /E:/workspace/java/Test.classLast modified 2018-1-29; size 405 bytesMD5 checksum f8f6002de3931b2e95125679f2ce1f6cCompiled from "Test.java"
public class Testminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref #5.#17 #2 = Class #18 #3 = Methodref #2.#19 #4 = Class #20 #5 = Class #21 #6 = Utf8 <init>#7 = Utf8 ()V#8 = Utf8 Code#9 = Utf8 LineNumberTable#10 = Utf8 get#11 = Utf8 ()I#12 = Utf8 StackMapTable#13 = Class #18 #14 = Class #22 #15 = Utf8 SourceFile#16 = Utf8 Test.java#17 = NameAndType #6:#7 #18 = Utf8 java/lang/Exception#19 = NameAndType #23:#7 #20 = Utf8 Test#21 = Utf8 java/lang/Object#22 = Utf8 java/lang/Throwable#23 = Utf8 printStackTrace
{public Test();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 4: returnLineNumberTable:line 1: 0public int get();descriptor: ()Iflags: ACC_PUBLICCode:stack=1, locals=4, args_size=10: iconst_01: istore_12: iconst_23: ireturn4: astore_15: aload_16: invokevirtual #3 9: iconst_110: istore_211: iconst_212: ireturn13: astore_314: iconst_215: ireturnException table:from to target type0 2 4 Class java/lang/Exception0 2 13 any4 11 13 anyLineNumberTable:line 4: 0line 9: 2line 5: 4line 6: 5line 7: 9line 9: 11StackMapTable: number_of_entries = 2frame_type = 68 stack = [ class java/lang/Exception ]frame_type = 72 stack = [ class java/lang/Throwable ]
}
SourceFile: "Test.java"
直接看重点 Exception table,字节码其他内容不做解释,想了解请移步Class文件格式,并参考JVM字节码手册
Exception table:from to target type0 2 4 Class java/lang/Exception0 2 13 any4 11 13 any
JVM8虚拟机规范中的Code属性的标准结构如下:
{ u2 start_pc;u2 end_pc;u2 handler_pc;u2 catch_type;} exception_table[exception_table_length];
看代码就已经比较好理解了:从start_pc(开始的pc指针)执行到end_pc(结束的pc指针),假如发生了catch_type类型的异常,就跳转到异常处理的pc指针处执行(handler_pc)
从0到2执行,要是有java/lang/Exception异常,就跳转到target(目标)4行执行;假如是其他的异常(any),就跳到13行执行;同时发生java/lang/Exception异常后,执行4-11行时假如又发生任意异常(any),就跳到13行执行。
jvm虚拟机就是这样通过异常表来执行的。
因此上述字节码中,有三段重复的字节码执行,带有3个return指令,第一个是没有异常情况下执行的,第二个是我们遇到在catch代码块中指定的异常情况下执行的,第三个是遇到我们未指定的异常情况下执行的。
总结一下,在编译时候,由于JVM没有那么智能,不能像人一样识别finally,因此我们把finally中的代码块放到了try语句块的return之前,并且把try代码块中return的value保存下来,之后根据以下情况(下一段落)来做处理,所以如果未发生异常,那么字节码会顺序执行。如果有异常,那么根据这个异常是否属于在catch中,跳转执行不同的字节码段
try catch finally和return其他状况总结如下:
try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况:
情况一:如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。
情况二:如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。
情况三:如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况,:
1)如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
2)如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。