java finally塊執(zhí)行時(shí)機(jī)全面分析
java里 finally 關(guān)鍵字通常與try catch塊一起使用。用來在方法結(jié)束前或發(fā)生異常時(shí)做一些資源釋放的操作。最近也看到網(wǎng)上有一些討論try catch finally關(guān)鍵詞執(zhí)行的順序的文章,并給出了finally塊是在方法最后執(zhí)行的。
這些觀點(diǎn)普遍認(rèn)為:
1) finally關(guān)鍵詞是在程序return語句后返回上一級方法前執(zhí)行的,其中返回值會保存在一個(gè)臨時(shí)區(qū)域,待執(zhí)行完finally塊的部分后,在將臨時(shí)區(qū)域的值返回。
2) 若finally塊里有返回值會替換掉程序中前面的try 或catch塊中return語句存放在臨時(shí)區(qū)域的值。
但是問題真的是這樣的嗎,我們仔細(xì)的想想,jvm是在運(yùn)行時(shí)對字節(jié)碼指令進(jìn)行解釋執(zhí)行的,當(dāng)他在執(zhí)行到return語句后,他哪知道后面有沒有finally塊,如果沒有finally塊怎么辦,不管是字節(jié)碼指令還是計(jì)算機(jī)的指令應(yīng)該是明確的,jvm沒有那么智能,同一個(gè)指令必須是明確的,不會包含兩層含義。所以對于return語句在運(yùn)行時(shí)不管什么情況,統(tǒng)一會彈出棧的內(nèi)容并返回到調(diào)用方法。
與此同時(shí),我們可以看到《深入java虛擬機(jī)》這一本書中給出了另外一種解釋。在java編譯器編譯finally子句時(shí)會生成jsr指令,它使jvm調(diào)轉(zhuǎn)到微型子例程進(jìn)行執(zhí)行,也就是finally塊處,同時(shí)將程序中的return 0語句編譯為在調(diào)用jsr指令前棧中的返回變量到局部變量,調(diào)用jsr指令,執(zhí)行finally塊,finally塊返回,在將局部變量中的返回值壓入棧,執(zhí)行ireturn指令,從棧中彈出返回值,返回到調(diào)用方法,這里在執(zhí)行jsr指令前將返回值保存在局部變量中,是因?yàn)閒inally塊執(zhí)行的過程中可能發(fā)生異?;蛘哒f是也有返回值,只有這樣做才能保證最后程序執(zhí)行的一致性。由于《深入java虛擬機(jī)》寫的已經(jīng)也一些年代了,同時(shí)作者使用的jvm編譯器的實(shí)現(xiàn)及版本與本文討論的也有差別。所以經(jīng)過測試,對于同一程序不同的編譯器實(shí)現(xiàn)或版本不同的字節(jié)碼的生成稍微有些差別。有興趣可以看看這本書中finally子句生成的字節(jié)碼。
本文的字節(jié)碼生成使用的是Oracle的jdk8u-25版本的編譯器編譯生成的。
下面我們來看一個(gè)實(shí)例。
1.try catch finally 示例:
public class FinallyTest {
public static void main(String[] args) {
int r = test();
System.out.println(r);
}
public static int test()
{
try {
System.out.println("try");
//return 1/0;
return 0;
} catch (Exception e) {
System.out.println("exception");
return 100;
}finally{
System.out.println("finally");
}
}
}
try塊中使用return 0語句,程序的運(yùn)行結(jié)果是:
try
finally
0
try塊中使用 return 1/0 語句,程序運(yùn)行的結(jié)果是:
exception
finally
100
其實(shí)通過運(yùn)行結(jié)果我們可以看出的是finally塊是在try或catch塊中的return語句前其他語句后執(zhí)行的。也就是說程序的書寫順序與我們執(zhí)行順序不符,因?yàn)閖vm是對字節(jié)碼進(jìn)行解釋執(zhí)行的,那么我們需要看看java編譯器是如何編譯這段代碼的,看看其生成的字節(jié)碼究竟是什么樣的。
2.程序生成的部分字節(jié)碼:(java字節(jié)碼指令請參考)
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=0
0: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #36 // String try
5: invokevirtual #38 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc #41 // String finally
13: invokevirtual #38 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: iconst_0
17: ireturn
18: astore_0
19: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #43 // String exception
24: invokevirtual #38 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
30: ldc #41 // String finally
32: invokevirtual #38 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: bipush 100
37: ireturn
38: astore_1
39: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #41 // String finally
44: invokevirtual #38 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: aload_1
48: athrow
Exception table:
from to target type
0 8 18 Class java/lang/Exception
0 8 38 any
18 27 38 any
從紅色的部分我們可以看出:10,11行對應(yīng)的是finally塊語句指令,16,17對應(yīng)的是return 0指令,在try塊其他語句之后,return之前。而19,20對應(yīng)的是finally塊指令,21,22對應(yīng)的是return 100語句的指令,在catch其他語句之后,return之前,由此我們可以看出這些背后發(fā)生的一切是java編譯器為我們做了這一切,至于程序中發(fā)生的異常,jvm會從異常表找到對應(yīng)處理異常的地址位置執(zhí)行。
因此我們可以得出結(jié)論finally塊中的語句會由java編譯器插入到try塊和catch塊return語句之前,其他語句之后。在這里也沒有生成jsr調(diào)用的子例程。所以才發(fā)生不管是執(zhí)行try塊還是執(zhí)行catch塊,最終在方法返回前都會執(zhí)行finally塊。
以上這篇java finally塊執(zhí)行時(shí)機(jī)全面分析就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
eclipse啟動(dòng)tomcat無法訪問的解決方法
這篇文章介紹了eclipse啟動(dòng)tomcat無法訪問的解決方法,有需要的朋友可以參考一下2013-10-10
mybatis中使用oracle關(guān)鍵字出錯(cuò)的解決方法
這篇文章主要給大家介紹了關(guān)于mybatis中使用oracle關(guān)鍵字出錯(cuò)的解決方法,文中通過示例代碼將解決的方法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-08-08
基于SpringBoot和Vue實(shí)現(xiàn)頭像上傳與回顯功能
在現(xiàn)代Web應(yīng)用中,用戶個(gè)性化體驗(yàn)尤為重要,其中頭像上傳與回顯是一個(gè)常見的功能需求,本文將詳細(xì)介紹如何使用Spring Boot和Vue.js構(gòu)建一個(gè)前后端協(xié)同工作的頭像上傳系統(tǒng),并實(shí)現(xiàn)圖片的即時(shí)回顯,需要的朋友可以參考下2024-08-08
Struts2 文件上傳進(jìn)度條的實(shí)現(xiàn)實(shí)例代碼
本篇文章主要介紹了Struts2 文件上傳進(jìn)度條的實(shí)現(xiàn)實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05

