1. 程式人生 > >Java基礎——靜態程式碼塊、構造程式碼塊、建構函式以及Java類初始化順序

Java基礎——靜態程式碼塊、構造程式碼塊、建構函式以及Java類初始化順序

閱讀目錄

靜態程式碼塊:用staitc宣告,jvm載入類時執行,僅執行一次
構造程式碼塊:類中直接用{}定義,每一次建立物件時執行。
執行順序優先順序:靜態塊,main(),構造塊,構造方法。

 

建構函式

public HelloA(){//建構函式
    }

關於建構函式,以下幾點要注意:
1.物件一建立,就會呼叫與之相應的建構函式,也就是說,不建立物件,建構函式時不會執行的。
2.建構函式的作用是用於給物件進行初始化。
3.一個物件建立,建構函式只執行一次,而一般方法可以被該物件呼叫多次。

 

構造程式碼塊

{//構造程式碼塊    
}

關於構造程式碼塊,以下幾點要注意:

  1. 構造程式碼塊的作用是給物件進行初始化。
  2. 物件一建立就執行構造程式碼塊了,而且優先於建構函式執行。這裡要強調一下,有物件建立,才會執行構造程式碼塊,類不能呼叫構造程式碼塊的,而且構造程式碼塊與建構函式的執行順序是前者先於後者執行
  3. 構造程式碼塊與建構函式的區別是:構造程式碼塊是給所有物件進行統一初始化,而建構函式是給對應的物件初始化,因為建構函式是可以多個的,執行哪個建構函式就會建立什麼樣的物件,但無論建立哪個物件,都會先執行相同的構造程式碼塊。也就是說,構造程式碼塊中定義的是不同物件共性的初始化內容。

靜態程式碼塊

static {//靜態程式碼塊    
}

關於靜態程式碼塊,要注意的是:

  1. 它是隨著類的載入而執行,只執行一次,並優先於主函式。具體說,靜態程式碼塊是由類呼叫的。類呼叫時,先執行靜態程式碼塊,然後才執行主函式的。
  2. 靜態程式碼塊其實就是給類初始化的,而構造程式碼塊是給物件初始化的
  3. 靜態程式碼塊中的變數是區域性變數,與普通函式中的區域性變數性質沒有區別。
  4. 一個類中可以有多個靜態程式碼塊

 

public class Test{
staitc int cnt=6;
static{
      cnt+=9;
}
public static void main(String[] args) {
      System.out.println(cnt);
}
static{
      cnt/=3;
}
}
執行結果:
5

Java類初始化順序

## 對於一個類的情況

例子1: 執行結果: A的靜態程式碼塊

public class HelloA {
    public HelloA(){//建構函式
        System.out.println("A的建構函式");    
    }
    {//構造程式碼塊
        System.out.println("A的構造程式碼塊");    
    }
    static {//靜態程式碼塊
        System.out.println("A的靜態程式碼塊");        
    }
    public static void main(String[] args) {
    }
}

例子2:

public class HelloA {
    public HelloA(){//建構函式
        System.out.println("A的建構函式");    
    }
    {//構造程式碼塊
        System.out.println("A的構造程式碼塊");    
    }
    static {//靜態程式碼塊
        System.out.println("A的靜態程式碼塊");        
    }
    public static void main(String[] args) {
        HelloA a=new HelloA();    
    }
}

執行結果:

A的靜態程式碼塊

A的構造程式碼塊

A的建構函式

 

 

例子3:

 

public class HelloA {
    public HelloA(){//建構函式
        System.out.println("A的建構函式");    
    }
    {//構造程式碼塊
        System.out.println("A的構造程式碼塊");    
    }
    static {//靜態程式碼塊
        System.out.println("A的靜態程式碼塊");        
    }
    public static void main(String[] args) {
        HelloA a=new HelloA();
        HelloA b=new HelloA();
    }

}
執行結果:
A的靜態程式碼塊
A的構造程式碼塊
A的建構函式
A的構造程式碼塊
A的建構函式

對於一個類而言,按照如下順序執行:

  1. 執行靜態程式碼塊
  2. 執行構造程式碼塊
  3. 執行建構函式

對於靜態變數、靜態初始化塊、變數、初始化塊、構造器,它們的初始化順序依次是(靜態變數、靜態初始化塊)>(變數、初始化塊)>構造器。

例子4:

public class InitialOrderTest {
        /* 靜態變數 */
    public static String staticField = "靜態變數";
        /* 變數 */
    public String field = "變數";
        /* 靜態初始化塊 */
    static {
        System.out.println( staticField );
        System.out.println( "靜態初始化塊" );
    }
        /* 初始化塊 */
    {
        System.out.println( field );
        System.out.println( "初始化塊" );
    }
        /* 構造器 */
    public InitialOrderTest()
    {
        System.out.println( "構造器" );
    }


    public static void main( String[] args )
    {
        new InitialOrderTest();
    }
}

執行以上程式碼,我們會得到如下的輸出結果:

  1. 靜態變數

  2. 靜態初始化塊

  3. 變數

  4. 初始化塊

  5. 構造器

## 對於繼承情況

例子5:

public class HelloA {
    public HelloA(){//建構函式
        System.out.println("A的建構函式");    
    }
    {//構造程式碼塊
        System.out.println("A的構造程式碼塊");    
    }
    static {//靜態程式碼塊
        System.out.println("A的靜態程式碼塊");        
    }
}
public class HelloB extends HelloA{
    public HelloB(){//建構函式
        System.out.println("B的建構函式");    
    }
    {//構造程式碼塊
        System.out.println("B的構造程式碼塊");    
    }
    static {//靜態程式碼塊
        System.out.println("B的靜態程式碼塊");        
    }
    public static void main(String[] args) {
        HelloB b=new HelloB();        
    }
}

執行結果:

A的靜態程式碼塊

B的靜態程式碼塊

A的構造程式碼塊

A的建構函式

B的構造程式碼塊

B的建構函式

 

當涉及到繼承時,按照如下順序執行:

  1. 執行父類的靜態程式碼塊,並初始化父類靜態成員變數
  2. 執行子類的靜態程式碼塊,並初始化子類靜態成員變數
  3. 執行父類的構造程式碼塊,執行父類的建構函式,並初始化父類普通成員變數
  4. 執行子類的構造程式碼塊, 執行子類的建構函式,並初始化子類普通成員變數

Java初始化順序如圖:

 

例子6:

class Parent {
        /* 靜態變數 */
    public static String p_StaticField = "父類--靜態變數";
         /* 變數 */
    public String    p_Field = "父類--變數";
    protected int    i    = 9;
    protected int    j    = 0;
        /* 靜態初始化塊 */
    static {
        System.out.println( p_StaticField );
        System.out.println( "父類--靜態初始化塊" );
    }
        /* 初始化塊 */
    {
        System.out.println( p_Field );
        System.out.println( "父類--初始化塊" );
    }
        /* 構造器 */
    public Parent()
    {
        System.out.println( "父類--構造器" );
        System.out.println( "i=" + i + ", j=" + j );
        j = 20;
    }
}

public class SubClass extends Parent {
         /* 靜態變數 */
    public static String s_StaticField = "子類--靜態變數";
         /* 變數 */
    public String s_Field = "子類--變數";
        /* 靜態初始化塊 */
    static {
        System.out.println( s_StaticField );
        System.out.println( "子類--靜態初始化塊" );
    }
       /* 初始化塊 */
    {
        System.out.println( s_Field );
        System.out.println( "子類--初始化塊" );
    }
       /* 構造器 */
    public SubClass()
    {
        System.out.println( "子類--構造器" );
        System.out.println( "i=" + i + ",j=" + j );
    }


        /* 程式入口 */
    public static void main( String[] args )
    {
        System.out.println( "子類main方法" );
        new SubClass();
    }
}

結果:

父類--靜態變數
父類--靜態初始化塊
子類--靜態變數
子類--靜態初始化塊
子類main方法
父類--變數
父類--初始化塊
父類--構造器
i=9, j=0
子類--變數
子類--初始化塊
子類--構造器
i=9,j=20

子類的靜態變數和靜態初始化塊的初始化是在父類的變數、初始化塊和構造器初始化之前就完成了。靜態變數、靜態初始化塊,變數、初始化塊初始化了順序取決於它們在類中出現的先後順序。

### 分析

  • (1)訪問SubClass.main(),(這是一個static方法),於是裝載器就會為你尋找已經編譯的SubClass類的程式碼(也就是SubClass.class檔案)。在裝載的過程中,裝載器注意到它有一個基類(也就是extends所要表示的意思),於是它再裝載基類。不管你創不建立基類物件,這個過程總會發生。如果基類還有基類,那麼第二個基類也會被裝載,依此類推。

  • (2)執行根基類的static初始化,然後是下一個派生類的static初始化,依此類推。這個順序非常重要,因為派生類的“static初始化”有可能要依賴基類成員的正確初始化。

  • (3)當所有必要的類都已經裝載結束,開始執行main()方法體,並用new SubClass()建立物件。

  • (4)類SubClass存在父類,則呼叫父類的建構函式,你可以使用super來指定呼叫哪個建構函式。基類的構造過程以及構造順序,同派生類的相同。首先基類中各個變數按照字面順序進行初始化,然後執行基類的建構函式的其餘部分。

  • (5)對子類成員資料按照它們宣告的順序初始化,執行子類建構函式的其餘部分。


參考文章連結:

java-靜態程式碼塊,構造程式碼塊,建構函式

Java類初始化順序

 

-------------我是低調的分割線--------------------------