Java中的return关键字
关于在Java在异常中使用 return
关键字的情形我在网上搜索的一下,看了一会就感觉有点晕了,感觉大部分讲的不是很好。有一些博客甚至把这个当作一个概念或者特性来进行记忆了,在什么什么情况下该怎么怎么样,完全是靠死记硬背来区分的。
我觉得死记硬背是不对的,所以我就尝试使用反编译字节码来观察指令的方式来观察这个现象,发现问题其实很简单。比如我们先举个例子:
1 | public class Test { |
请问这个例子中最终打印的 test()
方法的返回值 j
的值是多少呢?
想要知道结果,其实并不需要复杂的人肉去分析整个流程,只需要简单的反编译一下字节码就足够了。
我们先对源码进行编译:
javac Test.java
编译得到了 Test.class
文件,我们可以先执行一下看看
java Test
执行的结果是 100,那么这个结果是怎么得到的呢?我们尝试对字节码文件进行反编译
javap -c -private Test
反编译后我们得到如下结果注1
1 | Compiled from "Test.java" |
反编译得到的信息比较多,很多部分是我们不关心的,我们重点关心 test()
方法内部的执行过程,即第20~27行:
0: bipush 100
2: istore_0
3: iload_0
4: istore_1
5: sipush 300
8: istore_0
9: iload_1
10: ireturn
我们接下来了解下这几句指令做了什么事情:
- 向操作数栈中压入立即数 100
- 把操作数栈的栈顶元素弹出并且赋值给局部变量表中的第0个变量
- 把局部变量表中第0个元素压入栈顶
- 把栈顶元素弹出并赋值给局部变量表中的第1个变量
- 向操作数栈中压入立即数300
- 把栈顶元素赋值给局部变量表的第0个变量
- 把局部变量表中的第1个元素压入栈顶
- 从当前方法返回栈顶的int元素
以上指令执行的过程中局部变量和操作数栈的状态如下图所示:
我们重点发现在第4条指令中我们把栈顶元素(即 100)存入了第1个局部变量中,之后在第7条指令中又把第1个局部变量的值压入栈顶,之后立即执行 ireturn
指令把栈顶元素从当前方法返回。从分析中我们可以很清晰的得出返回值就是100的结论,有了指令的描述我们就不再需要看着源代码来猜测方法的运行结果了。
这里的例子是 return
关键字在正常的执行流程中生效的情况,对于 return
在异常或者finally中生效的情况,我们根本无需猜测或者查阅资料,只需要对源代码生成的字节码进行反编译即可。另外两种情况就留给读者自己去练习了,操作方式和和上面的一模一样,只需要仔细观察在执行 ireturn
的时候栈顶元素的值就可以了。
注1:如果对Java指令不熟悉可以参考《深入理解Java虚拟机:JVM高级特性与最佳实践》的附录B,如果没有书的话可以参考此链接:https://segmentfault.com/a/1190000008722128