貌似很多人對下面的方法的返回值都比較迷糊:
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.
爲什麼?
用javap -verbose ReturnValueTest 查看字節碼:
重點查看test()方法指令:下載
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並不影響返回值.