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

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

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

    一葉笑天
    雄關漫道真如鐵, 而今邁步從頭越。 從頭越, 蒼山如海, 殘陽如血。
    posts - 73,comments - 7,trackbacks - 0
    原文無處考證出處,本文轉貼自:http://www.itpub.net/thread-129367-1-1.html

    當年,國際巨星成龍的「龍種」曝光,眾人指責他對不起嬌妻林鳳嬌,逼得他出面召開記者會,向世人自白他犯了「全世界所有男人都會犯的錯誤」。從來沒犯過這種錯誤的我,也因此常常認為自己不是個男人。

    雖然沒犯過「全世界所有男人都會犯的錯誤」,但是我倒是曾經犯了「全世界所有程序員都會犯的錯誤」。不管使用何種語言,全世界所有程序員都一定犯過這種錯誤,那就是:太依賴編譯器,卻不知道編譯器做了哪些事。

    一般來說,越高階的程序語言,會提供越多語法上的便利,以方便程序撰寫,這就俗稱為syntactic sugar,我稱其為「語法上的甜頭」。雖說是甜頭,但是如果你未能了解該語法的實質內涵,很可能會未嘗甜頭,卻吃盡苦頭。

    不久前,我收到一個電子郵件,讀者列出下面的Java程序,向我求救。看過這個程序之后,我確定這又是一個「全世界所有程序員都會犯的錯誤」。

    // 程序1
    class Singleton {
      private static Singleton obj = new Singleton();
      public static int counter1;
      public static int counter2 = 0;
      private Singleton() {
        counter1++;
        counter2++;
      }
      public static Singleton getInstance() {
        return obj;
      }
    }

    // 程序2
    public class MyMain {
      public static void main(String[] args) {
        Singleton obj = Singleton.getInstance();
        System.out.println("obj.counter1=="+obj.counter1);
        System.out.println("obj.counter2=="+obj.counter2);
      }
    }

    執行結果是:
    obj.counter1==1
    obj.counter2==0

    你有沒有被此結果嚇一跳?乍看程序代碼,你很可能會認為counter1和counter2的值一定會相等,但執行結果顯然不是如此。其實,程序1被編譯后的程序應該等同于下面的程序3:

    // 程序3
    class Singleton {
      private static Singleton obj;
      public static int counter1;
      public static int counter2;
      static { // 這就是class constructor
        // 在進入此class constructor之前,class已經被JVM
        // 配置好內存,所有的static field都會被先設定為0,
        // 所以此時counter1和counter2都已經是0,且singleton為null
        obj = new Singleton(); // 問題皆由此行程序產生
        // counter1不會在此被設定為0
        counter2 = 0; // counter2再被設定一次0(其實是多此一舉)
      }
      private Singleton() { // 這是instance constructor
        counter1++;
        counter2++;
      }
      public static Singleton getInstance() {
        return obj;
      }
    }

    這是因為:當class具有static field,且直接在宣告處透過「=...」的方式設定其值時,編譯器會自動將這些敘述依序搬到class constructor內。同樣地,當class具有instance field,且直接在宣告處透過「=...」的方式設定其值時,編譯器會自動將這些敘述依序搬到instance constructor內。

    此程序在class constructor內,還未將static field初始化時(這時候,counter1和counter2都是0),就呼叫instance constructor,而instance constructor竟然還會去更動static field的值,使得counter1和counter2都變成1。然后instance constructor執行完,回到class constructor,再把counter2的值設為0(但是
    counter1維持不變)。最后的結果:counter1等于1,counter2等于0。

    欲改正程序1,方法有三:

    -方法一:將singleton field的宣告調到counter1與counter2 field之后。
                      這是最好的作法。
    -方法二:將counter2=0的宣告中,「=0」的部分刪除。這種作法只有在希望
    -方法三:將初始化的動作搬到class constructors內,自行撰寫,而不依賴
                      編譯器產生。這是最保險的作法。

    如何避免犯下「全世界所有程序員都會犯的錯誤」,我給各位Java程序員
    的建議是:
    -熟讀Java Language Specification
    -在有疑問時,使用J2SDK所提供的javap來反組譯Java Bytecode,直接觀察
    編譯后的結果。

    下面是我用javap來反組譯程序1的示范:

    C:\>javap -c -classpath . Singleton

    Compiled from MyMain.java
    class Singleton extends java.lang.Object {
        public static int counter1;
        public static int counter2;
        public static Singleton getInstance();
        static {};
    }

    Method Singleton()
       0 aload_0
       1 invokespecial #1 <Method java.lang.Object()>
       4 getstatic #2 <Field int counter1>
       7 iconst_1
       8 iadd
       9 putstatic #2 <Field int counter1>
      12 getstatic #3 <Field int counter2>
      15 iconst_1
      16 iadd
      17 putstatic #3 <Field int counter2>
      20 return

    Method Singleton getInstance()
       0 getstatic #4 <Field Singleton obj>
       3 areturn

    Method static {}
       0 new #5 <Class Singleton>
       3 dup
       4 invokespecial #6 <Method Singleton()>
       7 putstatic #4 <Field Singleton obj>
      10 iconst_0
      11 putstatic #3 <Field int counter2>
      14 return

    其實Java的syntactic sugar并不算多,C#的syntactic sugar才真的是無所不在,
    也因此C#的初學者更容易犯了「全世界所有程序員都會犯的錯誤」。許多C#的書都會一邊介紹C#語法,一邊介紹編譯之后MSIL(.NET的中間語言,類似Java的Bytecode)的結果,然而Java的書卻鮮少這么做。

    雖說是「全世界所有程序員都會犯的錯誤」,但是這不代表你犯了此錯誤之后,仍可以同愛借錢的曹啟泰一般地「抬頭挺胸、理直氣壯」。只要有心,其實這一類的錯誤仍是可以避免的。


    本文作者:蔡學鏞
    文章出處:Sleepless 2.0
    發表日期:03/10/2003

    posted on 2008-09-01 13:33 一葉笑天 閱讀(152) 評論(0)  編輯  收藏 所屬分類: JAVA技術
    主站蜘蛛池模板: 在线观看日本免费a∨视频| 美女黄色免费网站| 免费无码一区二区三区| 亚洲熟女少妇一区二区| 国产高清对白在线观看免费91| www亚洲精品少妇裸乳一区二区| 亚洲人成电影网站色www| 国产精品va无码免费麻豆| 未满十八私人高清免费影院| 国产免费av一区二区三区| 免费看一级毛片在线观看精品视频| 亚洲A丁香五香天堂网| 国产免费内射又粗又爽密桃视频 | 在线观看免费播放av片| 亚洲av最新在线网址| 99精品在线免费观看| 亚洲综合色区中文字幕| 日韩免费在线观看| 免费手机在线看片| 无码乱人伦一区二区亚洲一 | 日本高清免费不卡在线| 欧洲精品码一区二区三区免费看 | 亚洲中文字幕久在线| 日韩免费毛片视频| 三级毛片在线免费观看| 亚洲第一网站免费视频| 在线观看永久免费视频网站| av网站免费线看| 2022年亚洲午夜一区二区福利 | 亚洲人成网站999久久久综合| www.亚洲精品| 久久久久久国产精品免费免费男同| 亚洲精品无码久久毛片波多野吉衣| 成人a视频片在线观看免费| 免费福利在线观看| 久久久国产精品亚洲一区| 国产资源免费观看| 性无码免费一区二区三区在线| 亚洲无吗在线视频| 亚洲欧洲日产国码无码网站| 国内精品免费麻豆网站91麻豆|