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