JAVA談?wù)劗?dāng)return提前返回時的finally

常見的一個完整的try catch finally結(jié)構(gòu)語句是這樣的

try{}
catch(){}
finally{}

有一點(diǎn)基礎(chǔ)的讀者都會對finally有一點(diǎn)理解:

  1. 如果在try塊,catch塊中return茧痒,會執(zhí)行完finally塊語句后再返回
  2. 如果在try塊,catch塊中拋出了未受檢查的異常,會執(zhí)行完finally塊語句后再拋出異常

基于這兩點(diǎn),如果程序到達(dá)try塊,那么finally塊的代碼則一定會執(zhí)行,所以finally塊常用來安全的清理資源,鎖釋放贤徒。

但是finally還有一些細(xì)節(jié)需要小心芹壕,不然可能會得到你意想不到的結(jié)果
我們先上一段代碼,請讀者猜測命令行輸出的結(jié)果

public class TestClass {
    public int m1(){
        int x;
        try{
            x = 1;
            throw new Exception();
        } catch (Exception e){
            x = 2;
            return x;
        }finally {
            x = 3;
        }
    }

    public int[] m2(){
        int[] x = new int[2];
        try{
            x[0] = 1;
            throw new Exception();
        } catch (Exception e){
            x[1] = 2;
            return x;
        }finally {
            x[1] = 3;
        }
    }

    public String m3(){
        String x = null;
        try{
            x = "1";
            throw new Exception();
        } catch (Exception e){
            x = "2";
            return x;
        }finally {
            x += "3";
        }
    }

    public static void main(String[] args) {
        TestClass tc = new TestClass();
        System.out.println("m1():" + tc.m1());
        System.out.println("m2()[1]:" + tc.m2()[1]);
        System.out.println("m3():" + tc.m3());
    }
}

最后的返回值是

m1():2
m2()[1]:3
m3():2

如果返回值和你預(yù)期的一樣接奈,那么說明你已經(jīng)理解了我將要說的內(nèi)容踢涌,可以不用繼續(xù)往下看了

首先來看第一個方法

    public int m1(){
        int x;
        try{
            x = 1;
            throw new Exception();
        } catch (Exception e){
            x = 2;
            return x;
        }finally {
            x = 3;
        }
    }

或許有的讀者預(yù)期的結(jié)果是

m1():3

不是說finally塊一定會執(zhí)行嗎,為什么x的值沒有發(fā)生變化,finally塊確實(shí)執(zhí)行了,但是為什么x還是等于2
這里就是我今天要說的第一點(diǎn)序宦,當(dāng)try塊或者catch塊中提前返回時,finally塊的語句不會改變return的返回值
具體來看catch塊和finally塊的字節(jié)碼睁壁,不想看字節(jié)碼,想直接看結(jié)果的可以跳過這個部分

字節(jié)碼分析 ↓↓

//catch塊
// x=2
11: iconst_2
12: istore_1
13: iload_1
14: istore_3
//finally塊
// x=3
15: iconst_3
16: istore_1
17: iload_3
//return
18: ireturn

為了便于理解互捌,我先給出局部變量表1和3位置的含義(不準(zhǔn)確堡僻,因?yàn)榫植孔兞勘硎强梢詮?fù)用的,因此可能這個時候代表的是變量x下一個時刻代表y,但這個例子里局部變量表沒有被復(fù)用)

下標(biāo) 0 1 2 3
含義 int x int returnValue

字節(jié)碼的部分我簡單解釋一下
第11疫剃、12的意思就是將數(shù)字2存到局部變量表1的位置
第13、14行的意思是從局部變量表中1的位置讀取數(shù)字2硼讽,然后再將數(shù)字2存到局部變量表3的位置
此時局部變量表的數(shù)值為

下標(biāo) 0 1(x) 2 3(returnValue)
數(shù)值 int 2 int 2

第15巢价、16行的意思就是將數(shù)字3存到局部變量表1的位置
此時局部變量表的數(shù)值為

下標(biāo) 0 1(x) 2 3(returnValue)
數(shù)值 int 3 int 2

第17、18行的意思就是讀取局部變量表3位置的數(shù)字作為返回值返回

因此這里看出固阁,當(dāng)語句要執(zhí)行finally塊時壤躲,它先把準(zhǔn)備返回的數(shù)值2臨時保存在了3這個位置,當(dāng)執(zhí)行完finally塊之后备燃,回來把3位置的值給返回了

字節(jié)碼分析 ↑↑

我分析一下過程:

  1. 首先程序捕獲到異常箍铭,進(jìn)入了catch塊
  2. 執(zhí)行到x=2之后剖毯,想繼續(xù)執(zhí)行return方法直接返回
  3. 這時候finally語句塊說,你等等,我還有方法沒執(zhí)行亮钦,先不要返回,這時return語句想秉馏,虛擬機(jī)要求我必須執(zhí)行finally渊季,那好吧,我去執(zhí)行finally語句塊测垛,我把準(zhǔn)備返回的值2先存放在returnValue里面,等我執(zhí)行完了再回來返回
  4. 執(zhí)行完finally塊
  5. return回來捏膨,把剛才暫放在returnValue的值2取出來返回

由上面可以看出,finally不管執(zhí)行什么語句,到最后return總是返回已經(jīng)提前準(zhǔn)備好的returnValue,finally語句不會改變catch塊中的返回值

接下來我們來看第二個方法的代碼

    public int[] m2(){
        int[] x = new int[2];
        try{
            x[0] = 1;
            throw new Exception();
        } catch (Exception e){
            x[1] = 2;
            return x;
        }finally {
            x[1] = 3;
        }
    }

這里輸出的x[1] 為什么又是3而不是2了呢食侮,不是說finally不會改變return的返回值嗎

m2()[1]:3

這里是我要說的第二點(diǎn),JAVA中只有值傳遞号涯,沒有引用傳遞,對于引用類型(例如數(shù)組,對象)锯七,仍然也是值傳遞链快,只是這個值傳遞的是對象的地址
那我們繼續(xù)按照剛才的思路,當(dāng)return準(zhǔn)備返回時,先去執(zhí)行finally塊語句起胰,并且把準(zhǔn)備返回的值保存在了retunValue里面


這時returnValue保存的是數(shù)組的首地址:0XABCDEF久又,最后返回值0XABCDEF時不變的,但是可以改變對象的狀態(tài)巫延,使數(shù)組int[1]的更改為3。

此時返回的仍然是0XABCDEF地消,但數(shù)組對象的狀態(tài)已經(jīng)被改變了炉峰。

接下來我們來看第三個方法的代碼

    public String m3(){
        String x = null;
        try{
            x = "1";
            throw new Exception();
        } catch (Exception e){
            x = "2";
            return x;
        }finally {
            x += "3";
        }
    }

綜合以上兩點(diǎn),有經(jīng)驗(yàn)的讀者可能已經(jīng)明白了脉执,為什么m3方法返回的是2而不是12

m3():2

這里就是我的說的第三點(diǎn),不可變類型只能改變引用不能改變狀態(tài),所謂不可變類型不僅是用final修飾的類疼阔,還要求類的域是不可變的,對于String類

x += "3";

這一步會被分解為三個操作

  1. 獲取x的值"1"
  2. 生成一個新的String對象半夷,對象的值為"1" + "2"即"12"
  3. 將新的String對象的引用替換x的引用

在這個方法中婆廊,首先是會將x的地址保存到returnValue


而finally的語句x+= "3"會生成一個新的String對象,并將x的引用指向它


最后return返回的returnValue仍然是"2"這個字符串

總結(jié)

最后總結(jié)一下巫橄,當(dāng)在

try{}
catch(){return}
finally{}

try{return}
catch(){}
finally{}

的結(jié)構(gòu)中

  1. finally塊的代碼一定會執(zhí)行
  2. finally塊的代碼不會改變try塊catch塊的返回值
  3. 如果try塊catch塊return的是引用類型的地址淘邻,finally塊可以改變返回對象的狀態(tài)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市湘换,隨后出現(xiàn)的幾起案子宾舅,更是在濱河造成了極大的恐慌,老刑警劉巖彩倚,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筹我,死亡現(xiàn)場離奇詭異,居然都是意外死亡帆离,警方通過查閱死者的電腦和手機(jī)蔬蕊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哥谷,“玉大人岸夯,你說我怎么就攤上這事∶峭祝” “怎么了囱修?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長王悍。 經(jīng)常有香客問我破镰,道長,這世上最難降的妖魔是什么压储? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任鲜漩,我火速辦了婚禮,結(jié)果婚禮上集惋,老公的妹妹穿的比我還像新娘孕似。我一直安慰自己,他們只是感情好刮刑,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布喉祭。 她就那樣靜靜地躺著养渴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泛烙。 梳的紋絲不亂的頭發(fā)上理卑,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天,我揣著相機(jī)與錄音蔽氨,去河邊找鬼藐唠。 笑死,一個胖子當(dāng)著我的面吹牛鹉究,可吹牛的內(nèi)容都是我干的宇立。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼自赔,長吁一口氣:“原來是場噩夢啊……” “哼妈嘹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绍妨,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蟋滴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痘绎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肖粮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年孤页,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩馆。...
    茶點(diǎn)故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡行施,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魂那,到底是詐尸還是另有隱情蛾号,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布涯雅,位于F島的核電站鲜结,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏活逆。R本人自食惡果不足惜精刷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔗候。 院中可真熱鬧怒允,春花似錦、人聲如沸锈遥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丽惶,卻和暖如春炫七,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚊夫。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工诉字, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人知纷。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓壤圃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親琅轧。 傳聞我的和親對象是個殘疾皇子伍绳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評論 2 361