摘要:刷面試題偶然看到這類問題中含有時(shí)的執(zhí)行順序,覺得挺有意思于是小小的研究了一下,希望經(jīng)過我添油加醋天馬行空之后,能給你帶來一定的幫助原題里有一個(gè)語句,那么緊跟在這個(gè)后的里的代碼會不會被執(zhí)行什么時(shí)候被執(zhí)行在前還是后乍一看題目很簡單嘛,規(guī)范都說了
刷java面試題偶然看到這類問題(try/finally中含有return時(shí)的執(zhí)行順序),覺得挺有意思于是小小的研究了一下,希望經(jīng)過我添油加醋天馬行空之后,能給你帶來一定的幫助
原題try {} 里有一個(gè)return語句,那么緊跟在這個(gè)try后的finally {}里的代碼會不會被執(zhí)行?什么時(shí)候被執(zhí)行?在return前還是后?
乍一看題目很簡單嘛,java規(guī)范都說了,finally會在try代碼塊的return之前執(zhí)行,你這文章寫得沒意義,不看了
你等等!(拿起我身邊的五尺砍刀)
神奇栗子看完這個(gè)栗子,你在想想執(zhí)行順序到底是怎樣的
栗子代碼public static void main(String[] args) { int result = test(); System.out.println(result); } public static int test() { int t = 0; try { return t; } finally { ++t; } }分析一下
test()方法內(nèi),在try中return了t,那么在main方法中test()函數(shù)的返回值應(yīng)該是t=0,即控制臺輸出0
但是因?yàn)橛衒inally的存在,而finally中對t進(jìn)行了自增運(yùn)算,并且finally會在try中的return語句之前執(zhí)行,所以正確的情況是控制臺輸出1
所以你最終確定的答案是:控制臺輸出1
然而事實(shí)并非如此,將程序跑起來之后,得到的結(jié)果是:
輸出0
將栗子跑起來親眼看一下吧~
得到這個(gè)結(jié)果你也許要爆炸了,啥?java規(guī)范說的都是錯(cuò)的?!
不用急,到我給sun洗地的時(shí)間了
洗地時(shí)間在洗地之前,你很有必要先理解java中的值傳遞,如果你已經(jīng)了解該內(nèi)容可略過下面這一個(gè)小節(jié)點(diǎn)
java中的值傳遞由于這只是本文內(nèi)容引申出去的知識點(diǎn),不過多贅述,隨便嘮兩句,能借此明白則好,不明白希望借助搜索引擎明白一下!
在java的方法調(diào)用中,時(shí)常需要傳遞參數(shù),那么傳遞的參數(shù)是將之前的變量直接傳遞給方法內(nèi)了嗎?
顯然不是的,調(diào)用方法傳遞參數(shù)的時(shí)候,傳遞的只是原變量的一個(gè)副本(復(fù)制體),換句話說就是,將變量的值傳遞給了方法體,而并沒有真正的將變量傳遞進(jìn)去。
看個(gè)栗子:
public static void main(String[] args) { int t = 0; test(t); System.out.println(t); } public static int test(int a) { a = 111; }
正確輸出是0,因?yàn)閠est()方法內(nèi)拿到的a,只是t的一個(gè)副本(復(fù)制體)而并不直接是t,test()內(nèi)改變了a的值,并不影響t的值
以上是對于基本數(shù)據(jù)類型,如果對于對象呢?
如果參數(shù)是對象,那么傳遞的是對象的引用的副本(復(fù)制體),這也就是為什么在方法體內(nèi)對對象進(jìn)行修改,會真正的改變對象。因?yàn)榉椒w外的引用和方法體內(nèi)的引用指向的是堆內(nèi)存中的同一個(gè)對象,傳遞的是對象的引用
如果這里還不能理解值傳遞,建議先理解一下這一個(gè)概念再繼續(xù)往下看
真的開始分析了為了你看著方便,栗子代碼再來一份:(我真的不是為了湊字?jǐn)?shù))
public static void main(String[] args) { int result = test(); System.out.println(result); } public static int test() { int t = 0; try { return t; } finally { ++t; } }
當(dāng)代碼執(zhí)行到return t;時(shí),并不是直接將t返回了出去,而是將t保留了起來(因?yàn)檫€有一個(gè)finally語句塊沒有執(zhí)行!)
并且這個(gè)保留,就是值傳遞性質(zhì)的一個(gè)保留,也就是保留的是t的一個(gè)副本(復(fù)制體),我這里先叫他tt吧(不是套套??!)
接下來執(zhí)行finally語句塊,finally中將t做了自增運(yùn)算,t的確變成了1,但是這并沒有影響t的復(fù)制體tt的值!保留起來的tt值還是0!
這個(gè)時(shí)候執(zhí)行完了finally,正式將保留起來的tt返回出去,于是,整個(gè)函數(shù)的返回結(jié)果就是0
這個(gè)t的副本(復(fù)制體)保留的地方是哪兒呢?我查了半天,有個(gè)應(yīng)該靠譜的說法,保留在函數(shù)棧中,但具體保留的區(qū)域叫什么,我也不知道,還請知情大佬指教一下!
上圖或許直觀一點(diǎn)?
叫我一聲靈魂畫師我可敢答應(yīng)!
那么如果,這個(gè)t是一個(gè)對象呢?按照前面說的值傳遞的問題,如果t是一個(gè)對象,在finally中對t進(jìn)行修改,那么最終返回出去的t所顯示出來的數(shù)據(jù),應(yīng)該是經(jīng)過修改的。
寫一個(gè)Person類來檢驗(yàn)一下吧
public class Test { public static void main(String[] args) { Person result = test(); System.out.println(result.age); } public static Person test() { Person t = new Person(); t.age = 0; try { return t; } finally { t.age++; } } } class Person { int age; }
這段代碼輸出的是1,因?yàn)镻erson是一個(gè)類,t是一個(gè)對象的引用,對象實(shí)例保存在堆內(nèi)存中,t的副本tt也是一個(gè)對象的引用,t和tt都指向堆內(nèi)存中的對象實(shí)例,那么不論修改誰,實(shí)際上對象實(shí)例都被修改了!
看完我這一通胡說八道,你應(yīng)該了解了整個(gè)執(zhí)行流程咯?
那么繼續(xù)開一個(gè)引申
又一個(gè)小栗子如果在finally中也有一個(gè)return,會發(fā)生什么?
public static void main(String[] args) { int result = test(); System.out.println(result); } public static int test() { int t = 0; try { return t; } finally { ++t; return t; } }
最終輸出的結(jié)果是1
就是說,如果try中有return而finally中也有return,那么后者將會讓前者失效!
理解
總結(jié)=> try中將t保留了一份副本用于返回出去,到了finally中,又有一個(gè)return語句,這時(shí)候又要創(chuàng)建一個(gè)用于返回的副本,那這個(gè)時(shí)候就有兩個(gè)副本了,到底返回誰呢?取后者!
這一個(gè)面試題,看似簡單,卻暗藏殺機(jī)啊!
可是說了這么多,結(jié)果就是finally在return之后執(zhí)行嗎?
非也,你沒看見return沒有真正的執(zhí)行完就開始執(zhí)行finally嗎?并且是先執(zhí)行完了finally,才執(zhí)行完return,這也就很好理解java規(guī)范中的finally在return之前執(zhí)行了。
不過,按如上情況,這句話應(yīng)該變成這樣:finally比return先執(zhí)行完畢。是不是就更容易理解了呢?
也就是說,return先被執(zhí)行了,執(zhí)行return的時(shí)候發(fā)現(xiàn)有finally,于是不能那么快執(zhí)行完畢return,先去執(zhí)行finally,等finally執(zhí)行完畢之后,return才能執(zhí)行完畢。
全文下來,真是用我的三寸不爛之舌經(jīng)過滔滔不絕的輸出連綿不絕的蠱惑打開了你的新世界大門啊
結(jié)語更多內(nèi)容歡迎訪問我的主頁或我的博客
如果我的文章確實(shí)有幫助到你,請不要忘了點(diǎn)一下文末的"?"讓他變成"?"
作為一直雛雞難免很多地方理解不到位,文中若有錯(cuò)誤請直(bu)接(yao)指(ma)出(wo)
寫作不易!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/69950.html
摘要:基礎(chǔ)知識復(fù)習(xí)后端掘金的作用表示靜態(tài)修飾符,使用修飾的變量,在中分配內(nèi)存后一直存在,直到程序退出才釋放空間。將對象編碼為字節(jié)流稱之為序列化,反之將字節(jié)流重建成對象稱之為反序列化。 Java 學(xué)習(xí)過程|完整思維導(dǎo)圖 - 后端 - 掘金JVM 1. 內(nèi)存模型( 內(nèi)存分為幾部分? 堆溢出、棧溢出原因及實(shí)例?線上如何排查?) 2. 類加載機(jī)制 3. 垃圾回收 Java基礎(chǔ) 什么是接口?什么是抽象...
摘要:前兩天有粉絲聯(lián)系我,說他軟件工程專業(yè),大廠校招屢次被刷,有一個(gè)已經(jīng)到了三面,還是被刷了,感覺很絕望,不想找工作了。講一講協(xié)議的三次握手和四次揮手流程。什么是檢查異常,不受檢查異常,運(yùn)行時(shí)異常并分別舉例說明。 前兩天有粉絲聯(lián)系我,說他軟件工程專業(yè),大廠校招屢次被刷,有一個(gè)已經(jīng)到了三面,還是被刷...
摘要:當(dāng)運(yùn)行時(shí)系統(tǒng)遍歷調(diào)用棧而未找到合適的異常處理器,則運(yùn)行時(shí)系統(tǒng)終止。不可查異常編譯器不要求強(qiáng)制處置的異常包括運(yùn)行時(shí)異常與其子類和錯(cuò)誤。 目錄介紹 1.什么是異常 2.異常 2.1 異常的概述和分類【了解】 2.2 JVM默認(rèn)是如何處理異常的【理解】 2.3 異常處理的兩種方式【理解】 2.4 try...catch的方式處理異?!菊莆铡?2.5 編譯期異常和運(yùn)行期異常的區(qū)別【理解】...
摘要:前言最近參加了幾場面試,積累了一些高頻面試題,我把面試題分為兩類,一種是基礎(chǔ)試題主要考察前端技基礎(chǔ)是否扎實(shí),是否能夠?qū)⑶岸酥R體系串聯(lián)。 前言 最近參加了幾場面試,積累了一些高頻面試題,我把面試題分為兩類,一種是基礎(chǔ)試題: 主要考察前端技基礎(chǔ)是否扎實(shí),是否能夠?qū)⑶岸酥R體系串聯(lián)。一種是開放式問題: 考察業(yè)務(wù)積累,是否有自己的思考,思考問題的方式,這類問題沒有標(biāo)準(zhǔn)答案。 基礎(chǔ)題 題目的答...
閱讀 2415·2023-04-26 02:14
閱讀 2988·2021-09-30 09:46
閱讀 2197·2021-09-24 09:48
閱讀 1094·2021-09-24 09:47
閱讀 3315·2019-08-30 15:44
閱讀 1939·2019-08-30 15:44
閱讀 3348·2019-08-30 14:18
閱讀 2029·2019-08-30 12:58