貌似不少人對下面的方法的返回值都比較迷糊: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