public class TestReturn {
public static void main(String[] args) {
}
private static int testReturn1() {
int i = 0;
try {
i++;
return i; // 1
} catch (Exception e) {
i++;
} finally {
i++;
}
return i;
}
private Integer testReturn2() {
Integer i = 0;
try {
i++;
return i; // 2
} catch (Exception e) {
i++;
} finally {
i++;
}
return i;
}
private int testReturn3() {
int i = 0;
try {
i++;
int x = i / 0 ;
} catch (Exception e) {
i++;
return i; // 2
} finally {
i++;
}
return i;
}
private int testReturn4() {
int i = 0;
try {
i++;
return i;
} catch (Exception e) {
i++;
return i;
} finally {
i++;
return i; // 2
}
}
}
执行一下代码以便生成 TestReturn.class
。
方法一:打开 cmd
,并将窗口最大化(避免打印的反编译信息换行),进入 TestReturn.class
所在目录,执行命令 javap -v -p TestReturn
将字节码反编译成助记符。
方法二:借助于 IDEA
插件 jclasslib
查看字节码。
以 testReturn1()
方法为例来分析字节码,这里使用jclasslib
查看字节码。
Code
中的字节码页签就是字节码反编译后的字节码行号和助记符。
如果2~7
行出现 Exception
则跳转到 12
行进行处理,否则跳转到 22
行进行处理。
Code
中的 LineNumberTable
记录的是 PC
(程序计数器,也就是字节码行号指示器)对应的 Java
代码行号。
根据这个映射表,我们可以把字节码跟 Java
代码联系起来。
由上图可知,finally
一定会执行的语义是通过在 try
和 catch
后面添加 finally
的字节码实现的。
当 int
取值 -1~5
时,JVM
采用 iconst
指令将常量压入栈中。
弹出栈顶元素,并保存到局部变量表的 0
号槽。
0
号槽的变量加 1
。
将局部变量表 0
号槽的变量入栈,作为暂定的返回值。
弹出栈顶元素,并保存到局部变量表的 1
号槽,即保存暂定的返回值。
0
号槽的变量加 1
。
将局部变量表 1
号槽的变量入栈。
返回栈顶的值。
对于本代码,执行到这里就结束了,总体流程为:
try
→ finally
→ try
中的 retrun
。
将异常对象 e
保存到局部变量表的 1
号槽(覆盖掉暂定的返回值)。
由于2~7
行出现 Exception
都会跳转到 12
行进行处理,所以此时的状态不好确定,后面的就不分析了。
这里直接贴出 testReturn2()
跟字节码关联后的图。
当 int
取值 -1~5
时,JVM
采用 iconst
指令将常量压入栈中。
调用编号为 3
的类方法,也就是 Integer.valueOf
,将栈顶的基本类型转换为引用类型(以后缀 I
标识)。
将栈顶的引用类型保存到局部变量表的 1
号槽。
将局部变量表 1
号槽的引用类型压入栈中。
将栈顶的引用类型保存到局部变量表的 2
号槽。
注意:此时的两个引用指向同一个对象。
将局部变量表 1
号槽的引用类型压入栈中。
调用编号为 4
的实例方法,也就是 Integer.intValue
, 将栈顶的引用类型转换为基本类型。
将常量 1
压入栈中。
弹出栈顶两 int
类型数,相加后结果入栈。
将栈顶的基本类型转换为引用类型。
复制栈顶元素,并再次入栈,即此时栈中有两个相同的引用。
这两个引用与变量槽中的不同,用浅蓝色区分。
将栈顶的引用类型覆盖到局部变量表的 1
号槽。
将栈顶的引用类型保存到局部变量表的 3
号槽。
将局部变量表 2
号槽的引用类型压入栈中。
从栈顶弹出一个字长的元素,即弹出栈顶。
将局部变量表 1
号槽的引用类型压入栈中。
将栈顶的引用类型覆盖到局部变量表的 2
号槽。
将局部变量表 1
号槽的引用类型压入栈中,作为暂定的返回值。
将栈顶的引用类型覆盖到局部变量表的 3
号槽。
将局部变量表 1
号槽的引用类型压入栈中。
调用编号为 4
的实例方法,也就是 Integer.intValue
, 将栈顶的引用类型转换为基本类型。
将常量 1
压入栈中。
弹出栈顶两 int
类型数,相加后结果入栈。
将栈顶的基本类型转换为引用类型(以绿色标识)。
复制栈顶元素,并再次入栈,即此时栈中有两个相同的引用。
将栈顶的引用类型覆盖到局部变量表的 1
号槽。
将栈顶的引用类型保存到局部变量表的 4
号槽。
将局部变量表 3
号槽的引用类型压入栈中。
从栈顶弹出一个字长的元素,即弹出栈顶。
将局部变量表 2
号槽的引用类型压入栈中。
返回栈顶引用。
【Java】字节码层面理解try-catch-finally中的return问题
原文:https://www.cnblogs.com/ageovb/p/14942610.html