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

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

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

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評(píng)論 - 101, 引用 - 0
    數(shù)據(jù)加載中……

    全世界所有程序員都會(huì)犯的錯(cuò)誤-蔡學(xué)鏞

       當(dāng)年,國(guó)際巨星成龍的「龍種」曝光,眾人指責(zé)他對(duì)不起嬌妻林鳳嬌,逼得他出面召開記者會(huì),向世人自白他犯了「全世界所有男人都會(huì)犯的錯(cuò)誤」。從來沒犯過這種錯(cuò)誤的我,也因此常常認(rèn)為自己不是個(gè)男人。

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

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

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

    // 程序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);
    }
    }

    執(zhí)行結(jié)果是:
    obj.counter1==1
    obj.counter2==0

    你有沒有被此結(jié)果嚇一跳?乍看程序代碼,你很可能會(huì)認(rèn)為counter1和counter2的值一定會(huì)相等,但執(zhí)行結(jié)果顯然不是如此。其實(shí),程序1被編譯后的程序應(yīng)該等同于下面的程序3:

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

    這是因?yàn)椋寒?dāng)class具有static field,且直接在宣告處透過「=...」的方式設(shè)定其值時(shí),編譯器會(huì)自動(dòng)將這些敘述依序搬到class constructor內(nèi)。同樣地,當(dāng)class具有instance field,且直接在宣告處透過「=...」的方式設(shè)定其值時(shí),編譯器會(huì)自動(dòng)將這些敘述依序搬到instance constructor內(nèi)。

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

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

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

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

    下面是我用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

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

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


    本文作者:蔡學(xué)鏞
    文章出處:Sleepless 2.0
    發(fā)表日期:03/10/2003


    轉(zhuǎn)自:http://www.javaresearch.org/article/showarticle.jsp?column=544&thread=25732

    posted on 2005-04-20 00:21 weidagang2046 閱讀(212) 評(píng)論(1)  編輯  收藏 所屬分類: Java

    評(píng)論

    # re: 全世界所有程序員都會(huì)犯的錯(cuò)誤-蔡學(xué)鏞  回復(fù)  更多評(píng)論   

    第一次聽說class constructor的概念
    2005-04-20 00:22 | weidagang2046
    主站蜘蛛池模板: 亚洲精品永久在线观看| 亚洲免费视频网址| 国产免费高清69式视频在线观看| 精品久久洲久久久久护士免费| 亚洲中文字幕在线无码一区二区| 亚洲视频在线免费播放| 亚洲国产夜色在线观看| 久久国内免费视频| 亚洲人成欧美中文字幕| 国产免费一区二区三区VR| 91精品国产免费入口| 亚洲福利在线视频| 99re6热视频精品免费观看| 亚洲综合激情九月婷婷| 免费福利视频导航| 亚洲一区二区三区高清在线观看 | 在线a免费观看最新网站| 麻豆亚洲AV永久无码精品久久| 99re热精品视频国产免费| 亚洲精品中文字幕麻豆| 无码国产精品一区二区免费I6| 亚洲av午夜电影在线观看 | 亚洲综合一区二区精品导航| 中国xxxxx高清免费看视频| 亚洲综合av一区二区三区| 免费欧洲美女牲交视频| 国产一级高青免费| 亚洲国产精品乱码在线观看97| 最近2019中文字幕mv免费看| 国产精品亚洲一区二区三区| 色久悠悠婷婷综合在线亚洲| 亚洲免费在线视频| 国产L精品国产亚洲区久久| a级毛片在线免费看| 久久久无码精品亚洲日韩软件| a级毛片无码免费真人久久| 亚洲中字慕日产2021| 亚洲A丁香五香天堂网| 95免费观看体验区视频| 婷婷亚洲综合五月天小说在线| 四虎成人免费大片在线|