<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    問(wèn)題描述

    先來(lái)看一下以下的代碼,猜猜他們會(huì)是什么樣的結(jié)果:

      1 public class FinallyIssue {
      2     public static void main(String[] args) {
      3        System.out.println("finallyReturnTest : ");
      4        System.out.println("return value : " + finallyReturnTest(1));
      5        System.out.println("return value : " + finallyReturnTest(-1));
      6       
      7        System.out.println("finallyBreakTest : ");
      8        System.out.println("return value : " + finallyBreakTest(true));
      9        System.out.println("return value : " + finallyBreakTest(false));
     10       
     11        System.out.println("valueChangeInFinallyTest : ");
     12        System.out.println("return value : " + valueChangeInFinallyTest());
     13       
     14        System.out.println("valueChangeReturnInFinallyTest : ");
     15        System.out.println("return value : " + valueChangeReturnInFinallyTest());
     16       
     17        System.out.println("refValueChangeInFinallyTest : ");
     18        System.out.println("return name : " + refValueChangeInFinallyTest().name);
     19     }
     20    
     21     private static boolean finallyReturnTest(int value) {
     22        try {
     23            if(value > 0) {
     24               return true;
     25            } else {
     26               return false;
     27            }
     28        } finally {
     29            return false;
     30        }
     31     }
     32    
     33     private static boolean finallyBreakTest(boolean value) {
     34        while(value) {
     35            try {
     36               return true;
     37            } finally {
     38               break;
     39            }
     40        }
     41        return false;
     42     }
     43    
     44     private static int valueChangeInFinallyTest() {
     45        int i = 10;
     46        int j = 1;
     47        try {
     48            i = 100;
     49            j = 2;
     50            System.out.println("try : i = " + i);
     51            System.out.println("try : j = " + j);
     52            return i;
     53        } catch(Exception e) {
     54            e.printStackTrace();
     55        } finally {
     56            i = 1000;
     57            j = 3;
     58            System.out.println("finally : i = " + i);
     59            System.out.println("finally : j = " + j);
     60        }
     61       
     62        return i;
     63     }
     64    
     65     private static int valueChangeReturnInFinallyTest() {
     66        int i = 10;
     67        int j = 1;
     68        try {
     69            i = 100;
     70            j = 2;
     71            System.out.println("try : i = " + i);
     72            System.out.println("try : j = " + j);
     73            return i;
     74        } catch(Exception e) {
     75            e.printStackTrace();
     76        } finally {
     77            i = 1000;
     78            j = 3;
     79            System.out.println("finally : i = " + i);
     80            System.out.println("finally : j = " + j);
     81            return i;
     82        }
     83     }
     84    
     85     private static Person refValueChangeInFinallyTest() {
     86        Person p = new Person();
     87        try {
     88            p.name = "person1";
     89            System.out.println("try : Person name is : " + p.name);
     90            return p;
     91        } catch(Exception e) {
     92            e.printStackTrace();
     93        } finally {
     94            p.name = "person2";
     95            System.out.println("finally : Person name is : " + p.name);
     96        }
     97       
     98        p.name = "person3";
     99        System.out.println("out : Person name is : " + p.name);
    100       
    101        return p;
    102     }
    103    
    104     static class Person {
    105        public String name;
    106     }
    107 }

    這樣一段代碼的結(jié)果會(huì)是什么呢?

    以下是運(yùn)行結(jié)果:

    finallyReturnTest :

    return value : false

    return value : false

    finallyBreakTest :

    return value : false

    return value : false

    valueChangeInFinallyTest :

    try : i = 100

    try : j = 2

    finally : i = 1000

    finally : j = 3

    return value : 100

    valueChangeReturnInFinallyTest :

    try : i = 100

    try : j = 2

    finally : i = 1000

    finally : j = 3

    return value : 1000

    refValueChangeInFinallyTest :

    try : Person name is : person1

    finally : Person name is : person2

    return name : person2

     

    這個(gè)結(jié)果很出乎我的意料,我們知道finally總是會(huì)在try-catch語(yǔ)句塊執(zhí)行完后執(zhí)行,不管try語(yǔ)句塊中是否已經(jīng)返回或者拋出了異常。

     

    但是在上面的代碼測(cè)試中,如果finally語(yǔ)句塊中有returnbreakcontinue等語(yǔ)句,那么它們會(huì)覆蓋try語(yǔ)句塊中的returnbreakcontinue的語(yǔ)句,如以上的finallyReturnTest()finallyBreakTest()valueChangeReturnInFinallyTest()三個(gè)函數(shù)。

    另外,如果在finally語(yǔ)句塊中修改要返回的值類型變量的值,則這些修改不會(huì)保存下來(lái),如valueChangeInFinallyTest()函數(shù);如果要返回的值是引用類型,則修改引用類型的內(nèi)部成員的值會(huì)保存下來(lái)。

    如何解釋這個(gè)結(jié)果呢?

     

    問(wèn)題解釋

    結(jié)合《深入Java虛擬機(jī)(第二版)》這本書(shū)和代碼編譯后產(chǎn)生的二進(jìn)制指令代碼,我對(duì)以上問(wèn)題做了部分解釋,鑒于我的才疏學(xué)淺,有些觀點(diǎn)是有誤的,希望高手指正(有誤的觀點(diǎn)容易引起誤導(dǎo),這也是所以我一直非常小心,奈何水平有限,有些時(shí)候難免出錯(cuò))。

     

    在《深入Java虛擬機(jī)(第二版)》的第18章中提到,在早期的Java中,finally的行為是通過(guò)JSR指令來(lái)實(shí)現(xiàn)的,并且為這個(gè)指令引入了微型子程序的概念。我的理解,所謂微型子程序就是在函數(shù)A中嵌入一個(gè)不完整的函數(shù)B的調(diào)用。比如在這本書(shū)上的一個(gè)例子:

        private static int microSubroutine(boolean bValue) {
           
    try {
               
    if(bValue) {
                  
    return 1;
               }
               
    return 0;
           } 
    finally {
               System.out.println(
    "finally");
           }
        }

    會(huì)生成以下的二進(jìn)制代碼:

     0 iload_0

     1 ifeq 11

     4 iconst_1

     5 istore_1

     6 jsr 24

     9 iload_1

    10 ireturn

    11 iconst_0

    12 istore_1

    13 jsr 24

    16 iload_1

    17 ireturn

    18 astore_2

    19 jsr 24

    22 aload_2

    23 athrow

     

    24 astore_3

    25 getstatic #7 <Field java.io.PrintStream out>

    28 ldc #1 <String “finally”>

    30 invokevirtual #8 <Method void println(java.lang.String)>

    33 ret 3

     

    如上,24前綴的代碼行以后的部分就是微型子程序,在每一個(gè)出口之前都會(huì)用JSR調(diào)用這個(gè)微型子例程序,在這個(gè)微型子例程序返回(ret)后,返回調(diào)用JSR指令的下一條指令,然后返回(ireturnathrow)。

    jsr指令和ret指令的格式如下:

    jsr    branchbyte1, branchbyte2

    把返回地址壓棧,跳轉(zhuǎn)至((branchbyte1<<8) | branchbyte2)的位置繼續(xù)之行。

    ret index

    返回在index指示的局部變量中存儲(chǔ)的值(位置)。

     

    在上面的二進(jìn)制代碼中,每次通過(guò)jsr 24跳轉(zhuǎn)到微型子程序,它先將返回地址(jsr 24指令的下一條指令的地址)保存在index3的局部變量中,執(zhí)行完微型子程序后,通過(guò)ret 3返回到調(diào)用jsr 24指令的下一條指令執(zhí)行,并最終執(zhí)行返回。

     

    可是后來(lái)(有人說(shuō)是自1.4.2后),JVM中取消了jsr指令了,所有finally內(nèi)部的代碼都內(nèi)聯(lián)到源代碼中了(二進(jìn)制的源代碼)。所以以上的代碼在之后的編譯器中會(huì)產(chǎn)生如下的二進(jìn)制代碼:

         0 iload_0 [bValue]

         1 ifeq 14

         4 getstatic java.lang.System.out : java.io.PrintStream [16]

         7 ldc <String "finally"> [94]

         9 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]

        12 iconst_1

    13 ireturn

     

        14 getstatic java.lang.System.out : java.io.PrintStream [16]

        17 ldc <String "finally"> [94]

        19 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]

        22 iconst_0

    23 ireturn

     

        24 astore_1

        25 getstatic java.lang.System.out : java.io.PrintStream [16]

        28 ldc <String "finally"> [94]

        30 invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]

        33 aload_1

        34 athrow

     

    額,貌似有點(diǎn)偏題了,以上的描述是為了解釋《深入Java虛擬機(jī)(第二版)》中對(duì)finally描述過(guò)時(shí)的描述。下面讓我們來(lái)真正的解決這個(gè)問(wèn)題。還是從生成的Java二進(jìn)制代碼入手。

     

    首先來(lái)看一下valueChangeInFinallyTest()函數(shù)的二進(jìn)制代碼(注釋了打印語(yǔ)句,使代碼簡(jiǎn)潔):

             //int i = 10

         0 bipush 10

         2 istore_0 [i]

           //int j = 1

         3 iconst_1

         4 istore_1 [j]

           //i = 100

         5 bipush 100

         7 istore_0 [i]

           //j = 2

         8 iconst_2

         9 istore_1 [j]

           //保存i的值,因?yàn)樗且祷氐?/span>

        10 iload_0 [i]

    11 istore 4

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(開(kāi)始)----------------------

    //i = 1000

        13 sipush 1000

    16 istore_0 [i]

    //j = 3

        17 iconst_3

    18 istore_1 [j]

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(結(jié)束)----------------------

    //加載保存后的i的值,并返回。這里返回的是finally語(yǔ)句塊執(zhí)行前的i(由istore 4語(yǔ)句緩存起來(lái))的值,因而在finally語(yǔ)句塊中任何對(duì)i的操作并不會(huì)保留下來(lái)。這是在沒(méi)有異常發(fā)生的情況下。

        19 iload 4

    21 ireturn

     

        22 astore_2 [e]

        23 aload_2 [e]

    24 invokevirtual java.lang.Exception.printStackTrace() : void [104]

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(開(kāi)始)----------------------

        27 sipush 1000

        30 istore_0 [i]

        31 iconst_3

    32 istore_1 [j]

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(結(jié)束)----------------------

    33 goto 45

     

    36 astore_3

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(開(kāi)始)----------------------

        37 sipush 1000

        40 istore_0 [i]

        41 iconst_3

    42 istore_1 [j]

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(結(jié)束)----------------------

    //而在異常發(fā)生但沒(méi)有被正確處理的情況下,返回值已經(jīng)沒(méi)有什么意義了。

        43 aload_3

    44 athrow

     

    //這里是在有異常發(fā)生,并且異常得到了正確處理的情況下返回的,此時(shí)在finally語(yǔ)句塊中對(duì)i的操作就會(huì)保存下來(lái),并返回給調(diào)用者。

        45 iload_0 [i]

        46 ireturn

    相信以上的注釋已經(jīng)能很好的的解決這個(gè)問(wèn)題了(注:這里j的存在是為了證明在內(nèi)聯(lián)finally語(yǔ)句塊的時(shí)候,它只緩存返回值i,而無(wú)須緩存其他變量的值,如j的值)。需要特別注意的一點(diǎn)是,如果正常返回的話,finally語(yǔ)句塊中修改i的值是保存不下來(lái)的,但是如果出現(xiàn)異常,并被正常捕獲后,在finally語(yǔ)句塊中修改的i的值就會(huì)保存下來(lái)了。

     

    那么對(duì)valueChangeReturnInFinallyTest()函數(shù)中的現(xiàn)象如何解釋呢?對(duì)這個(gè)問(wèn)題解釋,首先要理解ireturn的指令。ireturn指令沒(méi)有操作數(shù),它把當(dāng)前操作棧的棧頂?shù)?/span>int值作為默認(rèn)的操作數(shù)。ireturn指令會(huì)彈出當(dāng)前棧頂?shù)?/span>int值,將其壓入調(diào)用者的操作棧中,同時(shí)忽略當(dāng)前操作棧中的其他值,即函數(shù)正常返回。因而如果在不優(yōu)化的情況下,在finally語(yǔ)句塊中的return語(yǔ)句會(huì)返回當(dāng)前棧頂?shù)?/span>int值(修改后的i值),然后函數(shù)返回,此時(shí)棧上的其他操作數(shù)就被忽略了,并且原本應(yīng)該執(zhí)行的ireturn語(yǔ)句也不會(huì)之行了。這種方式甚至?xí)雎話伋龅漠惓#词巩?dāng)前方法有異常拋出,它的調(diào)用方法還是認(rèn)為它正常返回了。

    如果查看優(yōu)化后的valueChangeReturnInFinallyTest()方法的二進(jìn)制源碼后,會(huì)發(fā)現(xiàn)當(dāng)前的代碼更加簡(jiǎn)潔了。但是它還是沒(méi)有避免在finally語(yǔ)句塊中使用return后,會(huì)忽略沒(méi)有捕獲到的異常的問(wèn)題。

             //int i = 10

         0 bipush 10

         2 istore_0 [i]

           //int j = 1

         3 iconst_1

         4 istore_1 [j]

           //i = 100

         5 bipush 100

         7 istore_0 [i]

           //j = 2

         8 iconst_2

         9 istore_1 [j]

    10 goto 22

    //catch block

        13 astore_2 [e]

        14 aload_2 [e]

        15 invokevirtual java.lang.Exception.printStackTrace() : void [104]

        18 goto 22

    21 pop

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(開(kāi)始)----------------------

    //i = 100

        22 sipush 1000

    25 istore_0 [i]

    //j = 3

        26 iconst_3

    27 istore_1 [j]

    //--------------------------------內(nèi)聯(lián)finally語(yǔ)句塊(結(jié)束)----------------------

    //返回finally語(yǔ)句塊中i的值

        28 iload_0 [i]

        29 ireturn

    經(jīng)過(guò)以上的解釋,我想對(duì)refValueChangeInFinallyTest()函數(shù)中的現(xiàn)象就比較好解釋了,因?yàn)楫?dāng)進(jìn)入finally語(yǔ)句塊的時(shí)候,保存的只是Person實(shí)例的一個(gè)引用,在finally語(yǔ)句塊中依然可以通過(guò)引用操作Person內(nèi)部成員的,因而在finally語(yǔ)句塊中的修改才能保存下來(lái)。

     

    而經(jīng)過(guò)編譯器優(yōu)化后的finallyReturnTest()finallyBreakTest()函數(shù)生成的二進(jìn)制代碼就成一樣的了:

         0 iload_0 [value]

         1 ifeq 8

         4 goto 8

         7 pop

         8 iconst_0

         9 ireturn

     

    后記

    原本以為這是一個(gè)小問(wèn)題的,沒(méi)想到花了我一個(gè)下午的時(shí)間才把問(wèn)題說(shuō)清楚了,而在描述問(wèn)題的過(guò)程中,我對(duì)問(wèn)題的本質(zhì)也看的更加清晰了。這個(gè)問(wèn)題開(kāi)始是源于我在論壇http://www.javaeye.com/topic/458668中看到,感覺(jué)論壇里面的人都沒(méi)很好的說(shuō)清楚這個(gè)問(wèn)題,剛好我看完了《深入Java虛擬機(jī)(第二版)》的書(shū),就把這個(gè)問(wèn)題完整的描述出來(lái)了。

                                                                                                                                                                             于2010年9月24日

    注:這些文章都是前些時(shí)候?qū)懙模安┛秃軄y,也都是隨便貼一些自己寫(xiě)的或轉(zhuǎn)載的,還有一些則是沒(méi)有貼出來(lái)過(guò)的。現(xiàn)在打算好好整理一下,完整的記錄自己的一些學(xué)習(xí)歷程,而每次看到過(guò)去的時(shí)間,則讓我想起以前的日子,因而我對(duì)時(shí)間一直是很重視的,所以每篇都著名寫(xiě)的日期,直到最先的文章出現(xiàn)。:)
    posted on 2011-06-24 22:37 DLevin 閱讀(2501) 評(píng)論(5)  編輯  收藏

    FeedBack:
    # re: finally知多少
    2011-06-25 22:17 | CodePro
    不錯(cuò),很深入  回復(fù)  更多評(píng)論
      
    # re: finally知多少
    2011-06-28 09:38 | hongfeng-Maple
    還是很不錯(cuò)的哈  回復(fù)  更多評(píng)論
      
    # re: finally知多少
    2011-06-28 14:44 | 雷斌輝
    不錯(cuò) 不錯(cuò)  回復(fù)  更多評(píng)論
      
    # re: finally知多少[未登錄](méi)
    2011-10-16 18:18 | chris
    這些天在學(xué)習(xí)字節(jié)碼相關(guān)的東東,很感謝樓主的分享,采用JDK1.6編譯了一下樓主的代碼,發(fā)現(xiàn)一下詭異的問(wèn)題:
    //加載保存后的i的值,并返回。這里返回的是finally語(yǔ)句塊執(zhí)行前的i(由istore 4語(yǔ)句緩存起來(lái))的值,因而在finally語(yǔ)句塊中任何對(duì)i的操作并不會(huì)保留下來(lái)。這是在沒(méi)有異常發(fā)生的情況下。

    19 iload 4
    21 ireturn

    我的控制臺(tái)顯示的iload0, 就是說(shuō)返回的是經(jīng)過(guò)finally語(yǔ)句賦值后的i, 而并不是在try塊中的i, 在eclipse調(diào)試運(yùn)行得出的也是相同的結(jié)果,不知是怎么回事  回復(fù)  更多評(píng)論
      
    # re: finally知多少[未登錄](méi)
    2011-10-16 18:37 | chris
    抱歉,樓主是對(duì)的,我運(yùn)行的是函數(shù)valueChangeReturnInFinallyTest,差別是如果finally里面有return語(yǔ)句的情況下,最終返回的是經(jīng)過(guò)finally修改的值,在此情況下執(zhí)行的字節(jié)碼為:
    iload0
    ireturn
    否則返回的為try塊中的值,執(zhí)行的字節(jié)碼為
    iload4
    ireturn  回復(fù)  更多評(píng)論
      

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 男人的天堂av亚洲一区2区| 午夜无码A级毛片免费视频 | 欧洲精品成人免费视频在线观看| 久久精品人成免费| 8x8x华人永久免费视频| 4399影视免费观看高清直播| 2015日韩永久免费视频播放| 9277手机在线视频观看免费| 95老司机免费福利| 黄+色+性+人免费| 美女视频黄的全免费视频网站| 我们的2018在线观看免费高清 | 黄人成a动漫片免费网站| 男人免费视频一区二区在线观看 | 亚洲H在线播放在线观看H| 亚洲最大成人网色香蕉| 亚洲熟妇无码一区二区三区 | 18亚洲男同志videos网站| 亚洲春色另类小说| 亚洲一久久久久久久久| 小说区亚洲自拍另类| 一本久久A久久免费精品不卡| 丁香花在线观看免费观看图片| 永久免费av无码入口国语片| 一级毛片免费视频| 在线观看免费人成视频| 国产国产成年年人免费看片| 亚洲日韩VA无码中文字幕| 亚洲成AV人在线播放无码| 亚洲视频免费在线看| 亚洲欧美日韩综合久久久久| 免费一级毛片在线播放放视频| h片在线观看免费| 84pao强力永久免费高清| 成人性生交视频免费观看| 免费人成视频在线观看视频| 国产亚洲人成网站观看| 亚洲人成网网址在线看| 99ee6热久久免费精品6| 亚洲av永久无码精品网址| 好吊色永久免费视频大全|