全世界所有程式員都會犯的錯誤 |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
http://www.oreilly.com.tw/sleepless/
http://www.oreilly.com.tw/sleepless2/mistake.html
全世界所有程式員都會犯的錯誤 當年,國際巨星成龍的「龍種」曝光,眾人指責他對不起嬌妻林鳳嬌,逼得他出面召開記者會,向世人自白他犯了「全世界所有男人都會犯的錯誤」。從來沒犯過這種錯誤的我,也因此常常認為自己不是個男人。 雖然沒犯過「全世界所有男人都會犯的錯誤」,但是我倒是曾經犯了「全世界所有程式員都會犯的錯誤」。不管使用何種語言,全世界所有程式員都一定犯過這種錯誤,那就是:太依賴編譯器,卻不知道編譯器做了哪些事。 一般來說,越高階的程式語言,會提供越多語法上的便利,以方便程式撰寫,這就俗稱為 syntax 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」的部分刪除。這種作法只有在希望 counter2 的初始值是 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哈哈 蔡學鏞先生太又趣啦 哈哈 哈哈哈 發表人 - conundrum 於 2005/02/07 13:53:35 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |