貌似不少人对下面的方法的返回值都比较迷糊:java
Java代码jvm
- package cc.lixiaohui.demo;
-
- public class ReturnValueTest {
- public int test() {
- int a;
- try {
- a = 1;
- //int b = 1 / 0;
- return a;
- } catch (Exception e) {
- a = 2;
- return a;
- } finally {
- a = 3;
- }
- }
- }
test方法的返回值天然是1,若是把注释那行去掉,那就是2.ui
为何?this
用javap -verbose ReturnValueTest 查看字节码:spa
重点查看test()方法指令: 下载htm
Javap代码ci
- Compiled from "ReturnValueTest.java"
- public class cc.lixiaohui.demo.ReturnValueTest extends java.lang.Object
- SourceFile: "ReturnValueTest.java"
- minor version: 0
- major version: 49
- Constant pool: --常量池
- const #1 = class #2; // cc/lixiaohui/demo/ReturnValueTest
- const #2 = Asciz cc/lixiaohui/demo/ReturnValueTest;
- const #3 = class #4; // java/lang/Object
- const #4 = Asciz java/lang/Object;
- const #5 = Asciz <init>;
- const #6 = Asciz ()V;
- const #7 = Asciz Code;
- const #8 = Method #3.#9; // java/lang/Object."<init>":()V
- const #9 = NameAndType #5:#6;// "<init>":()V
- const #10 = Asciz LineNumberTable;
- const #11 = Asciz LocalVariableTable;
- const #12 = Asciz this;
- const #13 = Asciz Lcc/lixiaohui/demo/ReturnValueTest;;
- const #14 = Asciz test;
- const #15 = Asciz ()I;
- const #16 = class #17; // java/lang/Exception
- const #17 = Asciz java/lang/Exception;
- const #18 = Asciz a;
- const #19 = Asciz I;
- const #20 = Asciz e;
- const #21 = Asciz Ljava/lang/Exception;;
- const #22 = Asciz SourceFile;
- const #23 = Asciz ReturnValueTest.java;
-
- {
- public cc.lixiaohui.demo.ReturnValueTest(); --构造方法就不分析了
- Code:
- Stack=1, Locals=1, Args_size=1
- 0: aload_0
- 1: invokespecial #8; //Method java/lang/Object."<init>":()V
- 4: return
- LineNumberTable:
- line 8: 0
-
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this Lcc/lixiaohui/demo/ReturnValueTest;
-
-
- public int test();
- Code:
- Stack=1, Locals=5, Args_size=1 -- [Stack=1貌似表示栈深度为1(不肯定),]Locals=5表示局部变量表长度为5, Args_size=1表示该方法有1个参数(this)
- 0: iconst_1 --将int值 1 压栈
- 1: istore_1 --将栈顶值(即1)弹出保存至局部变量表第2个位置(局部变量表下标是从0开始的,可是0位置被this变量占用了)
- 2: iload_1 --将局部变量表第2个位置的值压栈
- 3: istore 4 --将栈顶的值弹出并保存至局部变量表第5个位置(这里能够看到)
- 5: iconst_3 --(这里开始为finally块)将int值3压栈
- 6: istore_1 --将栈顶的值(即3)弹出并存储至局部变量表第2个位置
- 7: iload 4 --将局部变量表第5个位置(即1)压栈
- 9: ireturn --返回栈顶的值(即1), 结束方法调用(该路径为try(未抛异常) -> finally)
-
- 10: astore_2 --将栈顶的引用(这里即为catch块中捕捉到的异常e)存储至局部变量表的第3个位置
- 11: iconst_2 --将int值2压栈
- 12: istore_1 --将栈顶值(即2)弹出并存储至局部变量表第2个位置
- 13: iload_1 --将局部变量表第2个位置的值(即2)压栈
- 14: istore 4 --将栈顶值弹出(即2)并保存至局部变量表第5个位置,原来第五个位置是1,如今1被覆盖了,变为2
- 16: iconst_3 --(这里开始为finally块)将int值3压栈
- 17: istore_1 --将栈顶的值(即3)弹出并存储至局部变量表第2个位置
- 18: iload 4 --将局部变量表第5个位置(即2)压栈
- 20: ireturn --返回栈顶的值(即2),结束方法调用(该路径为try(抛Exception或其子类异常) -> catch -> finally)
-
- 21: astore_3 --将栈顶的引用(这里为非Exception的子类异常)存储至局部变量表的第4个位置
- 22: iconst_3 --将int值3压栈
- 23: istore_1 --将栈顶值(即3)弹出并存储至局部变量表第二个位置
- 24: aload_3 --将局部变量表第4个位置(即为异常引用)压栈
- 25: athrow --将栈顶的异常抛出(该路径为try(抛非Exception或其子类异常) -> finally, 或者try(抛Exception或其子类异常) -> catch(抛任何异常) -> finally )
- Exception table:
- from to target type
- 0 5 10 Class java/lang/Exception --若执行到0-5行(即在try块中)抛出java.lang.Exception或其子类则跳转至第10行执行(即catch块)
- 0 5 21 any --若执行到0-5行(即在try块中)抛出非java.lang.Exception或其子类则跳转至第21行执行(即finally块)
- 10 16 21 any --若执行到10-16行(即在catch块中)抛出任何异常则跳转至21行执行(即finally块)
- LineNumberTable: --这个表为源码行数与字节码行数的映射
- line 13: 0
- line 14: 2
- line 19: 5
- line 14: 7
- line 15: 10
- line 16: 11
- line 17: 13
- line 19: 16
- line 17: 18
- line 18: 21
- line 19: 22
- line 20: 24
-
- LocalVariableTable: --这个即为局部变量表, start和length结合起来就能够肯定该变量的做用范围, 例如this做用范围为整个方法
- Start Length Slot Name Signature
- 0 26 0 this Lcc/lixiaohui/demo/ReturnValueTest; --占用第1个slot(一个slot应该是32bits)
- 2 8 1 a I --I标识int, 占用第2个slot
- 13 8 1 a I --占用第2个slot
- 24 2 1 a I --占用第2个slot
- 11 10 2 e Ljava/lang/Exception; --占用第3个slot
-
-
- }
能够发现jvm始终把返回值放在最后一个局部变量表的位置,并且在finally中改变x并不影响返回值.get