Java中關(guān)于try、catch、finally中的細(xì)節(jié)分析
閱讀本文大概需要 7?分鐘。
來(lái)自:網(wǎng)絡(luò)
看了一位博友的一篇文章,講解的是關(guān)于java中關(guān)于try、catch、finally中一些問(wèn)題 下面看一個(gè)例子(例1),來(lái)講解java里面中try、catch、finally的處理流程 public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????return?t;
????????}?catch?(Exception?e)?{
????????????//?result?=?"catch";
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}首先程序執(zhí)行try語(yǔ)句塊,把變量t賦值為try,由于沒(méi)有發(fā)現(xiàn)異常,接下來(lái)執(zhí)行finally語(yǔ)句塊,把變量t賦值為finally,然后return t,則t的值是finally,最后t的值就是finally,程序結(jié)果應(yīng)該顯示finally,但是實(shí)際結(jié)果為try。 為什么會(huì)這樣,我們不妨先看看這段代碼編譯出來(lái)的class對(duì)應(yīng)的字節(jié)碼,看虛擬機(jī)內(nèi)部是如何執(zhí)行的。 我們用javap -verbose TryCatchFinally 來(lái)顯示目標(biāo)文件(.class文件)字節(jié)碼信息
系統(tǒng)運(yùn)行環(huán)境:mac os lion系統(tǒng) 64bit
jdk信息:Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-11M3527) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)
編譯出來(lái)的字節(jié)碼部分信息,我們只看test方法,其他的先忽略掉 public?static?final?java.lang.String?test();
??Code:
???Stack=1,?Locals=4,?Args_size=0
???0:????ldc????#16;?//String?
???2:????astore_0
???3:????ldc????#18;?//String?try
???5:????astore_0
???6:????aload_0
???7:????astore_3
???8:????ldc????#20;?//String?finally
???10:????astore_0
???11:????aload_3
???12:????areturn
???13:????astore_1
???14:????ldc????#22;?//String?catch
???16:????astore_0
???17:????aload_0
???18:????astore_3
???19:????ldc????#20;?//String?finally
???21:????astore_0
???22:????aload_3
???23:????areturn
???24:????astore_2
???25:????ldc????#20;?//String?finally
???27:????astore_0
???28:????aload_2
???29:????athrow
??Exception?table:
???from???to??target?type
????8????13???Class?java/lang/Exception
????8????24???any
???19????24???any
??LineNumberTable:?
???line?5:?0
???line?8:?3
???line?9:?6
???line?15:?8
???line?9:?11
???line?10:?13
???line?12:?14
???line?13:?17
???line?15:?19
???line?13:?22
???line?14:?24
???line?15:?25
???line?16:?28
??LocalVariableTable:?
???Start??Length??Slot??Name???Signature
?????27??????0????t???????Ljava/lang/String;
?????10??????1????e???????Ljava/lang/Exception;
??StackMapTable:?number_of_entries?=?2
???frame_type?=?255?/*?full_frame?*/
?????offset_delta?=?13
?????locals?=?[?class?java/lang/String?]
?????stack?=?[?class?java/lang/Exception?]
???frame_type?=?74?/*?same_locals_1_stack_item?*/
?????stack?=?[?class?java/lang/Throwable?]首先看LocalVariableTable信息,這里面定義了兩個(gè)變量 一個(gè)是t String類型,一個(gè)是e Exception 類型 接下來(lái)看Code部分 第[0-2]行,給第0個(gè)變量賦值“”,也就是String t=""; 第[3-6]行,也就是執(zhí)行try語(yǔ)句塊 賦值語(yǔ)句 ,也就是 t = "try"; 第7行,重點(diǎn)是第7行,把第s對(duì)應(yīng)的值"try"付給第三個(gè)變量,但是這里面第三個(gè)變量并沒(méi)有定義,這個(gè)比較奇怪 第[8-10] 行,對(duì)第0個(gè)變量進(jìn)行賦值操作,也就是t="finally" 第[11-12]行,把第三個(gè)變量對(duì)應(yīng)的值返回 通過(guò)字節(jié)碼,我們發(fā)現(xiàn),在try語(yǔ)句的return塊中,return 返回的引用變量(t 是引用類型)并不是try語(yǔ)句外定義的引用變量t,而是系統(tǒng)重新定義了一個(gè)局部引用t’,這個(gè)引用指向了引用t對(duì)應(yīng)的值,也就是try ,即使在finally語(yǔ)句中把引用t指向了值finally,因?yàn)閞eturn的返回引用已經(jīng)不是t ,所以引用t的對(duì)應(yīng)的值和try語(yǔ)句中的返回值無(wú)關(guān)了。 下面在看一個(gè)例子:(例2) public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????return?t;
????????}?catch?(Exception?e)?{
????????????//?result?=?"catch";
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這里稍微修改了 第一段代碼,只是在finally語(yǔ)句塊里面加入了 一個(gè) return t 的表達(dá)式。 按照第一段代碼的解釋,先進(jìn)行try{}語(yǔ)句,然后在return之前把當(dāng)前的t的值try保存到一個(gè)變量t',然后執(zhí)行finally語(yǔ)句塊,修改了變量t的值,在返回變量t。 這里面有兩個(gè)return語(yǔ)句,但是程序到底返回的是try 還是 finally。接下來(lái)我們還是看字節(jié)碼信息 public?static?final?java.lang.String?test();
??Code:
???Stack=1,?Locals=2,?Args_size=0
???0:????ldc????#16;?//String?
???2:????astore_0
???3:????ldc????#18;?//String?try
???5:????astore_0
???6:????goto????17
???9:????astore_1
???10:????ldc????#20;?//String?catch
???12:????astore_0
???13:????goto????17
???16:????pop
???17:????ldc????#22;?//String?finally
???19:????astore_0
???20:????aload_0
???21:????areturn
??Exception?table:
???from???to??target?type
????9?????9???Class?java/lang/Exception
???16????16???any
??LineNumberTable:?
???line?5:?0
???line?8:?3
???line?9:?6
???line?10:?9
???line?12:?10
???line?13:?13
???line?14:?16
???line?15:?17
???line?16:?20
??LocalVariableTable:?
???Start??Length??Slot??Name???Signature
?????19??????0????t???????Ljava/lang/String;
?????6??????1????e???????Ljava/lang/Exception;
??StackMapTable:?number_of_entries?=?3
???frame_type?=?255?/*?full_frame?*/
?????offset_delta?=?9
?????locals?=?[?class?java/lang/String?]
?????stack?=?[?class?java/lang/Exception?]
???frame_type?=?70?/*?same_locals_1_stack_item?*/
?????stack?=?[?class?java/lang/Throwable?]
???frame_type?=?0?/*?same?*/這段代碼翻譯出來(lái)的字節(jié)碼和第一段代碼完全不同,還是繼續(xù)看code屬性 第[0-2]行、[3-5]行第一段代碼邏輯類似,就是初始化t,把try中的t進(jìn)行賦值try 第6行,這里面跳轉(zhuǎn)到第17行,[17-19]就是執(zhí)行finally里面的賦值語(yǔ)句,把變量t賦值為finally,然后返回t對(duì)應(yīng)的值 我們發(fā)現(xiàn)try語(yǔ)句中的return語(yǔ)句給忽略??赡躩vm認(rèn)為一個(gè)方法里面有兩個(gè)return語(yǔ)句并沒(méi)有太大的意義,所以try中的return語(yǔ)句給忽略了,直接起作用的是finally中的return語(yǔ)句,所以這次返回的是finally。 接下來(lái)在看看復(fù)雜一點(diǎn)的例子:(例3) public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????Integer.parseInt(null);
????????????return?t;
????????}?catch?(Exception?e)?{
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????//?System.out.println(t);
????????????//?return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這里面try語(yǔ)句里面會(huì)拋出 java.lang.NumberFormatException,所以程序會(huì)先執(zhí)行catch語(yǔ)句中的邏輯,t賦值為catch,在執(zhí)行return之前,會(huì)把返回值保存到一個(gè)臨時(shí)變量里面t ',執(zhí)行finally的邏輯,t賦值為finally,但是返回值和t',所以變量t的值和返回值已經(jīng)沒(méi)有關(guān)系了,返回的是catch 例4: public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????Integer.parseInt(null);
????????????return?t;
????????}?catch?(Exception?e)?{
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這個(gè)和例2有點(diǎn)類似,由于try語(yǔ)句里面拋出異常,程序轉(zhuǎn)入catch語(yǔ)句塊,catch語(yǔ)句在執(zhí)行return語(yǔ)句之前執(zhí)行finally,而finally語(yǔ)句有return,則直接執(zhí)行finally的語(yǔ)句值,返回finally 例5: public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????Integer.parseInt(null);
????????????return?t;
????????}?catch?(Exception?e)?{
????????????t?=?"catch";
????????????Integer.parseInt(null);
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????//return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這個(gè)例子在catch語(yǔ)句塊添加了Integer.parser(null)語(yǔ)句,強(qiáng)制拋出了一個(gè)異常。然后finally語(yǔ)句塊里面沒(méi)有return語(yǔ)句。 繼續(xù)分析一下,由于try語(yǔ)句拋出異常,程序進(jìn)入catch語(yǔ)句塊,catch語(yǔ)句塊又拋出一個(gè)異常,說(shuō)明catch語(yǔ)句要退出,則執(zhí)行finally語(yǔ)句塊,對(duì)t進(jìn)行賦值。然后catch語(yǔ)句塊里面拋出異常。 結(jié)果是拋出java.lang.NumberFormatException異常 例子6: public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????Integer.parseInt(null);
????????????return?t;
????????}?catch?(Exception?e)?{
????????????t?=?"catch";
????????????Integer.parseInt(null);
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這個(gè)例子和上面例子中唯一不同的是,這個(gè)例子里面finally 語(yǔ)句里面有return語(yǔ)句塊。try catch中運(yùn)行的邏輯和上面例子一樣,當(dāng)catch語(yǔ)句塊里面拋出異常之后,進(jìn)入finally語(yǔ)句快,然后返回t。則程序忽略catch語(yǔ)句塊里面拋出的異常信息,直接返回t對(duì)應(yīng)的值 也就是finally。方法不會(huì)拋出異常 例子7: public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????Integer.parseInt(null);
????????????return?t;
????????}?catch?(NullPointerException?e)?{
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這個(gè)例子里面catch語(yǔ)句里面catch的是NPE異常,而不是java.lang.NumberFormatException異常,所以不會(huì)進(jìn)入catch語(yǔ)句塊,直接進(jìn)入finally語(yǔ)句塊,finally對(duì)s賦值之后,由try語(yǔ)句拋出java.lang.NumberFormatException異常。 例子8 public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";
????????????Integer.parseInt(null);
????????????return?t;
????????}?catch?(NullPointerException?e)?{
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}和上面的例子中try catch的邏輯相同,try語(yǔ)句執(zhí)行完成執(zhí)行finally語(yǔ)句,finally賦值s 并且返回s ,最后程序結(jié)果返回finally 例子9: public?class?TryCatchFinally?{
????@SuppressWarnings("finally")
????public?static?final?String?test()?{
????????String?t?=?"";
????????try?{
????????????t?=?"try";return?t;
????????}?catch?(Exception?e)?{
????????????t?=?"catch";
????????????return?t;
????????}?finally?{
????????????t?=?"finally";
????????????String.valueOf(null);
????????????return?t;
????????}
????}
????public?static?void?main(String[]?args)?{
????????System.out.print(TryCatchFinally.test());
????}
}這個(gè)例子中,對(duì)finally語(yǔ)句中添加了String.valueOf(null), 強(qiáng)制拋出NPE異常。首先程序執(zhí)行try語(yǔ)句,在返回執(zhí)行,執(zhí)行finally語(yǔ)句塊,finally語(yǔ)句拋出NPE異常,整個(gè)結(jié)果返回NPE異常。 對(duì)以上所有的例子進(jìn)行總結(jié)
try、catch、finally語(yǔ)句中,在如果try語(yǔ)句有return語(yǔ)句,則返回的之后當(dāng)前try中變量此時(shí)對(duì)應(yīng)的值,此后對(duì)變量做任何的修改,都不影響try中return的返回值
如果finally塊中有return 語(yǔ)句,則返回try或catch中的返回語(yǔ)句忽略。
如果finally塊中拋出異常,則整個(gè)try、catch、finally塊中拋出異常
所以使用try、catch、finally語(yǔ)句塊中需要注意的是
盡量在try或者catch中使用return語(yǔ)句。通過(guò)finally塊中達(dá)到對(duì)try或者catch返回值修改是不可行的。
finally塊中避免使用return語(yǔ)句,因?yàn)閒inally塊中如果使用return語(yǔ)句,會(huì)顯示的消化掉try、catch塊中的異常信息,屏蔽了錯(cuò)誤的發(fā)生
finally塊中避免再次拋出異常,否則整個(gè)包含try語(yǔ)句塊的方法回拋出異常,并且會(huì)消化掉try、catch塊中的異常
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。
朕已閱?

