1. 程式人生 > >java類的執行順序、語句執行順序詳解

java類的執行順序、語句執行順序詳解

本文講解Java中類的執行順序
順序應該是這樣的:父類Static->子類static->父類預設{}->父類建構函式->子類預設{}->子類建構函式
所以上面的例子列印順序應該是這樣的:
parent static block 父類Static
child static block 子類static
parent  block 父類預設{}
parent constructor 父類建構函式
child  block子類預設{}
child constructor子類建構函式
class Parent{
     static String name = "hello";
     static {
         System.out.println("parent static block");
     }
     {
         System.out.println("parent block");
     }
     public Parent(){
         System.out.println("parent constructor");
     }
 }
   
 class Child extends Parent{
     static String childName = "hello";
     static {
         System.out.println("child static block");
     }
     {
         System.out.println("child block");
     }
     public Child(){
         System.out.println("child constructor");
     }
 }
   
 public class StaticIniBlockOrderTest {
   
     public static void main(String[] args) {
         new Child();//語句(*)

     }

 }

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

程式如下

public class Teststaticblock
{
 public Teststaticblock()
 {
  this("second");
  System.out.println("beginconstructor");
  System.out.println(s_a);
  System.out.println(s_b);
  System.out.println(c);
  System.out.println(d);
//  this("second");//call to thismust be first statement in constructor
  s_a=1111;
  s_b=2222;
  c=3333;
  d=4444;
  System.out.println(s_a);
  System.out.println(s_b);
  System.out.println(c);
  System.out.println(d);
  System.out.println("endconstructor");
 }
 public Teststaticblock(String s)
 {
  System.out.println("beginsecond constructor");
  System.out.println("end secondconstructor");
 }
 public static void main(String args[])
 {
  System.out.println("beginmain");
  System.out.println(s_a);
  System.out.println(s_b);
//  System.out.println(c);//non-staticvariable c cannot be referenced from a static context
//  System.out.println(d);//non-staticvariable c cannot be referenced from a static context
  s_a=11111;
  s_b=22222;
//  c=33333;//non-static variablec cannot be referenced from a static context
//  d=44444;//non-static variablec cannot be referenced from a static context
  System.out.println(s_a);
  System.out.println(s_b);
//  System.out.println(c);//non-staticvariable c cannot be referenced from a static context
//  System.out.println(d);//non-staticvariable c cannot be referenced from a static context
  System.out.println("before newclass object");
  Teststaticblock t = newTeststaticblock();
  System.out.println("end newclass object");
  System.out.println(s_a);
  System.out.println(s_b);
//  System.out.println(c);//non-staticvariable c cannot be referenced from a static context
//  System.out.println(d);//non-staticvariable c cannot be referenced from a static context
  s_a=111111;
  s_b=222222;
//  c=333333;//non-staticvariable c cannot be referenced from a static context
//  d=444444;//non-staticvariable c cannot be referenced from a static context
  System.out.println(s_a);
  System.out.println(s_b);
//  System.out.println(c);//non-staticvariable c cannot be referenced from a static context
//  System.out.println(d);//non-staticvariable c cannot be referenced from a static context
  System.out.println("endmain");
 }
 
 static int s_a=1;
 int c=3;
 {
  System.out.println("beginblock");
  System.out.println(s_a);
  System.out.println(s_b);
  System.out.println(c);
//  System.out.println(d);//illegalforward reference
  s_a=111;
  s_b=222;
  c=333;
  d=444;
  System.out.println(s_a);
  System.out.println(s_b);
  System.out.println(c);
//  System.out.println(d);//illegalforward reference
  System.out.println("endblock");
 }
 static
 {
  System.out.println("beginstatic block");
  System.out.println(s_a);
//  System.out.println(s_b);//illegalforward reference
//  System.out.println(c);//non-staticvariable c cannot be referenced from a static context
//  System.out.println(d);//non-staticvariable c cannot be referenced from a static context
  s_a=11;
  s_b=22;
  System.out.println(s_a);
//  System.out.println(s_b);//illegalforward reference
//  System.out.println(c);//non-staticvariable c cannot be referenced from a static context
//  System.out.println(d);//non-staticvariable c cannot be referenced from a static context
  System.out.println("end staticblock");
 }
 int d=4;
 static int s_b=2;
 
}

輸出如下:

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、即使靜態域宣告中使用了顯式初始化語句(比如:intx=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 java2》書中所說的"進入構造方法後,如果第一句是呼叫別的構造方法,則進入別的構造方法。否則,執行例項塊"的提法有問題。事實是,不管是否使用this()都會先執行例項塊,再進入構造方法。另外,本程式需要在sdk1.4下編譯,在sdk1.3下編譯將不允許在靜態塊或例項塊中改變位置在它們後面宣告的域的值。