日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java 判断顺序_通过指令码来判断Java代码的执行顺序(++问题与return和finally的问题)...

發布時間:2025/3/12 java 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 判断顺序_通过指令码来判断Java代码的执行顺序(++问题与return和finally的问题)... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

問題

在《深入理解Java虛擬機》一書中遇到了如下代碼:

public int method() {

int i;

try {

i = 1;

return i;

} catch (Exception e) {

i = 2;

return i;

} finally {

i = 3;

}

}

由于曾經搜了一下return和finally的問題后,只是簡單的看到了finally會執行,從而導致自己誤以為只是簡單地把finally的執行順序放到return語句之前,因此判斷這段代碼的執行結果應該是3,可實際運行結果是1。研究后發現自己當初真是太糊涂,于是便記錄下來。

工具

我們都知道,class文件中的內容就是可供JVM理解的字節碼,JVM也是根據class的字節碼來執行程序代碼,所以class文件中就包含著程序代碼最終的執行順序。

我們可以通過官方提供的javap -c 再加上class文件的路徑來得到各個方法對應的指令碼。

例如:javap -c Test.class

引例

由于是打算使用JVM的指令碼來解決這個問題,剛開始先以一個簡單的方法來說明一下。對于如下方法:

public int method1() {

int i = 1;

return i;

}

該方法對應的指令碼為:

public int method1();

Code:

0: iconst_1

1: istore_1

2: iload_1

3: ireturn

每個指令對應著一個操作,上面的指令碼意思是:

將int型數值0推送至棧頂

將棧頂int型元素存入第二個空間中

將第二個空間的int型元素推送至棧頂

返回將棧頂的int型元素并退出這個方法

由此可以看出,通過指令碼,我們可以直觀地看到程序代碼的執行順序,這對于解決任何執行順序的問題是一個利器。

如果還是感覺有些不明所以,那我們可以再看看i++和++i的問題。對于如下代碼:

// return 1

public int method2() {

int i = 1;

return i++;

}

// return 2

public int method3() {

int i = 1;

return ++i;

}

它們的指令碼分別是:

public int method2();

Code:

0: iconst_1

1: istore_1

2: iload_1

3: iinc 1, 1

6: ireturn

public int method3();

Code:

0: iconst_1

1: istore_1

2: iinc 1, 1

5: iload_1

6: ireturn

顯然,這兩段指令碼最大的區別就是iinc 1,1指令的位置不同,而且如果把這條指令刪除,那么與method1的指令碼完全一致,對應源代碼來看,這條指令就是++這個符號的影響了。

而這個關鍵的iinc 1,1指令的作用哪怕完全不懂也能猜出來,就是將第二個空間的int數據+1后再放回第二個空間。

將這個含義放到指令碼中再重新捋一遍,以method2為例:

將int型數值0推送至棧頂

將棧頂int型元素存入第二個空間中

將第二個空間的int型元素(1)推送至棧頂

將第二個空間的int數據+1后再放回第二個空間

返回將棧頂的int型元素并退出這個方法

需要注意的是,第三步是將1而不是整個空間推送至棧頂,所以第四步對第二個空間中的數據1加1后并沒有改變棧頂的值,因此返回值為1。相對的,method2則是:

將int型數值0推送至棧頂

將棧頂int型元素存入第二個空間中

將第二個空間的int數據+1后再放回第二個空間

將第二個空間的int型元素(2)推送至棧頂

返回將棧頂的int型元素并退出這個方法

所以,返回的是2。

解決

現在我們可以看最初的method方法了,在這里再復制一遍代碼:

public int method() {

int i;

try {

i = 1;

return i;

} catch (Exception e) {

i = 2;

return i;

} finally {

i = 3;

}

}

對應的指令碼:

public int method();

Code:

0: iconst_1

1: istore_1

2: iload_1

3: istore 4

5: iconst_3

6: istore_1

7: iload 4

9: ireturn

10: astore_2

11: iconst_2

12: istore_1

13: iload_1

14: istore 4

16: iconst_3

17: istore_1

18: iload 4

20: ireturn

21: astore_3

22: iconst_3

23: istore_1

24: aload_3

25: athrow

Exception table:

from to target type

0 5 10 Class java/lang/Exception

0 5 21 any

10 16 21 any

這段指令碼不同的地方在于最后有一個異常表,我們先不用管它,先看到第一個ireturn指令的指令碼,即代碼中的第9行為止的指令碼:

0: iconst_1

1: istore_1

2: iload_1

3: istore 4

5: iconst_3

6: istore_1

7: iload 4

9: ireturn

這段指令碼就是當沒有異常時,程序執行的指令碼,finally語句塊的指令碼已經包含在里面了:

將int型數值1推送至棧頂

將棧頂int型元素存入第二個空間中

將第二個空間的int型元素(1)推送至棧頂

將棧頂int型元素存入第五個空間中

將int型數值3推送至棧頂

將棧頂int型元素存入第二個空間中(3)

將第五個空間的int型元素(1)推送至棧頂

返回將棧頂的int型元素并退出這個方法

由此可以看出,方法返回的是第五個空間的1而不是第二個空間的3,和運行結果一致。

其中,關鍵的地方就是第四步以及第七步。由此可見,Java程序在執行時遇到return語句時,會先將方法的返回值保存起來,如果還有finally語句塊,那么就先執行finally語句塊,最后再將返回值取出后返回。

另外,如果return后跟的是表達式或者方法,那么會先計算出最終的返回值后再執行finally語句塊,可自行驗證。

當然,如果保存的返回值是一個引用類型的變量,那么在finally代碼塊中修改則會改變這個變量本身的屬性,因而改變返回值的屬性,畢竟finally的代碼是的的確確執行過了。

例如,返回一個List,在finally中又對List進行了增加或刪除,那么返回的List的內容自然也變了。

附加

關于指令碼其余的部分,涉及到更多知識,在這里根據我的理解簡單說一下。

這段指令碼最后有一個異常表,它的含義可以簡單解釋為:在[from,to)的區間內,如果發生type類型的異常,那么就跳到target執行。

正因為有了異常表的存在,在出現異常時,程序可以根據產生的異常來跳到正確的位置執行接下來的代碼。

[10,20]即為catch代碼塊對應的指令碼,不過其中會把捕捉到的異常存儲下來,也就是源代碼中的Exception e。[21,25]則是會把try語句塊中拋出的catch沒有捕捉的異常保存下來,然后執行finally的代碼,最后拋出該異常結束方法。

這三片指令碼都包含了finally的指令碼,也就保證了源代碼中finally的代碼肯定會執行。

結論

Java程序在執行時遇到return語句時,會先將方法的返回值保存起來,如果還有finally語句塊,那么就先執行finally語句塊,最后再將返回值取出后返回。另外,如果return后跟的是表達式或者方法,那么會先計算出最終的返回值后再執行finally語句塊。

筆記內容只是本人思考而寫,如果有什么問題,還請指出,謝謝!

總結

以上是生活随笔為你收集整理的java 判断顺序_通过指令码来判断Java代码的执行顺序(++问题与return和finally的问题)...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。