程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> 論全世界所有程序員都會犯的錯誤

論全世界所有程序員都會犯的錯誤

編輯:JAVA編程入門知識

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

雖然沒犯過“全世界所有男人都會犯的錯誤”,但是我倒是曾經犯了“全世界所有程序員都會犯的錯誤”。不管使用何種語言,全世界所有程序員都一定犯過這種錯誤,那就是:太依靠編譯器,卻不知道編譯器做了哪些事。
  
  一般來說,越高階的程序語言,會提供越多語法上的便利,以方便程序撰寫,這就俗稱為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 :
  

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,直接觀察編譯後的結果。
  
進入討論組討論。
  
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved