域初始化、靜態塊及構造方法等在創建類實例時的執行順序

    在《Core java 2: volumn 1, Edition 5》一書的第四章“對象與類”中講到域賦值語句、實例塊、靜態塊及構造方法等在創建類實例時的執行順序,中文譯本有些處翻譯的不貼切,而英文原書中也有一處錯誤。本文通過一個小程序來說明類實例構造過程中的語句執行順序。

 

  1. package testcase;  
  2.  
  3. public class Teststaticblock {  
  4.     public Teststaticblock() {  
  5.         this("second");  
  6.         System.out.println("begin constructor");  
  7.         System.out.println(s_a);  
  8.         System.out.println(s_b);  
  9.         System.out.println(c);  
  10.         System.out.println(d);  
  11.         // this("second");//call to this must be first statement in constructor  
  12.         s_a = 1111;  
  13.         s_b = 2222;  
  14.         c = 3333;  
  15.         d = 4444;  
  16.         System.out.println(s_a);  
  17.         System.out.println(s_b);  
  18.         System.out.println(c);  
  19.         System.out.println(d);  
  20.         System.out.println("end constructor");  
  21.     }  
  22.  
  23.     public Teststaticblock(String s) {  
  24.         System.out.println("begin second constructor");  
  25.         System.out.println("end second constructor");  
  26.     }  
  27.  
  28.     public static void main(String args[]) {  
  29.         System.out.println("begin main");  
  30.         System.out.println(s_a);  
  31.         System.out.println(s_b);  
  32.         // System.out.println(c);//non-static variable c cannot be referenced  
  33.         // from a static context  
  34.         // System.out.println(d);//non-static variable c cannot be referenced  
  35.         // from a static context  
  36.         s_a = 11111;  
  37.         s_b = 22222;  
  38.         // c=33333;//non-static variable c cannot be referenced from a static  
  39.         // context  
  40.         // d=44444;//non-static variable c cannot be referenced from a static  
  41.         // context  
  42.         System.out.println(s_a);  
  43.         System.out.println(s_b);  
  44.         // System.out.println(c);//non-static variable c cannot be referenced  
  45.         // from a static context  
  46.         // System.out.println(d);//non-static variable c cannot be referenced  
  47.         // from a static context  
  48.         System.out.println("before new class object");  
  49.         Teststaticblock t = new Teststaticblock();  
  50.         System.out.println("end new class object");  
  51.         System.out.println(s_a);  
  52.         System.out.println(s_b);  
  53.         // System.out.println(c);//non-static variable c cannot be referenced  
  54.         // from a static context  
  55.         // System.out.println(d);//non-static variable c cannot be referenced  
  56.         // from a static context  
  57.         s_a = 111111;  
  58.         s_b = 222222;  
  59.         // c=333333;//non-static variable c cannot be referenced from a static  
  60.         // context  
  61.         // d=444444;//non-static variable c cannot be referenced from a static  
  62.         // context  
  63.         System.out.println(s_a);  
  64.         System.out.println(s_b);  
  65.         // System.out.println(c);//non-static variable c cannot be referenced  
  66.         // from a static context  
  67.         // System.out.println(d);//non-static variable c cannot be referenced  
  68.         // from a static context  
  69.         System.out.println("end main");  
  70.     }  
  71.  
  72.     static int s_a = 1;  
  73.     int c = 3;  
  74.     {  
  75.         System.out.println("begin block");  
  76.         System.out.println(s_a);  
  77.         System.out.println(s_b);  
  78.         System.out.println(c);  
  79.         // System.out.println(d);//illegal forward reference  
  80.         s_a = 111;  
  81.         s_b = 222;  
  82.         c = 333;  
  83.         d = 444;  
  84.         System.out.println(s_a);  
  85.         System.out.println(s_b);  
  86.         System.out.println(c);  
  87.         // System.out.println(d);//illegal forward reference  
  88.         System.out.println("end block");  
  89.     }  
  90.     static {  
  91.         System.out.println("begin static block");  
  92.         System.out.println(s_a);  
  93.         // System.out.println(s_b);//illegal forward reference  
  94.         // System.out.println(c);//non-static variable c cannot be referenced  
  95.         // from a static context  
  96.         // System.out.println(d);//non-static variable c cannot be referenced  
  97.         // from a static context  
  98.         s_a = 11;  
  99.         s_b = 22;  
  100.         System.out.println(s_a);  
  101.         // System.out.println(s_b);//illegal forward reference  
  102.         // System.out.println(c);//non-static variable c cannot be referenced  
  103.         // from a static context  
  104.         // System.out.println(d);//non-static variable c cannot be referenced  
  105.         // from a static context  
  106.         System.out.println("end static block");  
  107.     }  
  108.     int d = 4;  
  109.     static int s_b = 2;  
  110.  
  111. }  
結果如下: begin static block
1
11
end static block
begin main
11
2
11111
22222
before new class object
begin block
11111
22222
3
111
222
333
end block
begin second constructor
end second constructor
begin constructor
111
222
333
4
1111
2222
3333
4444
end constructor
end new class object
1111
2222
111111
222222
end main

通過對輸出進行分析,可以得出如下結果:
1、在類第一次加載時候,會執行靜態域(field)初始化語句和靜態塊(用static{}包含的部分)。
這裏要注意:
    a、不管靜態域聲明語句的實際位置在哪兒,當第一次加載類的時候都會首先對它初始化爲缺省值(0,false,null等)。
    b、即使靜態域聲明中使用了顯式初始化語句(比如:int x=3),第一次加載類的時候也會先把它初始化爲缺省值(此時x爲0),然後再按照下面說的要點c來執行賦值語句(x=3)。
    c、對於靜態域的顯式初始化語句和靜態塊,按照在類中代碼出現的先後順序執行。
     因此,在上面的例子程序中,我們看到
      static int s_a=1;
      static
      {
         s_a=11;
         s_b=22;
       }
       static int s_b=2;
      對s_a,s_b會有不同的效果。類加載時候,s_a,s_b都被初始化爲0,然後由於依照代碼順序執行了s_a=1;s_a=11;s_b=22;s_b=2;結果s_a、s_b分別變成了11和2。

2、當構造類實例時候,會先對實例域初始化爲缺省值,然後執行實例塊(用{}括起來的部分),然後執行構造方法。其中:
    a、如同1中一樣,如果有實例域的顯式初始化語句,程序仍然是先將該域初始化爲缺省值,然後按照代碼在類中出現的先後順序執行初始化語句或者實例塊。如果實例塊位置在初始化語句前面,即使它改變了該域的值,也會被隨後執行的初始化語句改回去。
    b、在進入構造方法後,如果構造方法第一句是使用this(...)調用另一構造方法的話,則先執行另一構造方法,然後再執行本構造方法的方法體。這種用法必須讓this(...)位於第一句。

《Core java 2》書中所說的"進入構造方法後,如果第一句是調用別的構造方法,則進入別的構造方法。否則,執行實例塊"的提法有問題。事實是,不管是否使用this()都會先執行實例塊,再進入構造方法。另外,本程序需要在sdk1.4下編譯,在sdk1.3下編譯將不允許在靜態塊或實例塊中改變位置在它們後面聲明的域的值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章