當前位置:才華齋>計算機>java語言>

Java類和物件的初始化順序

java語言 閱讀(8.74K)

在Java中,類裝載器把一個類裝入Java虛擬機器中,要經過三個步驟來完成:裝載、連結和初始化,其中連結又可以分成校驗、準備和解析三步,除了解析外,其它步驟是嚴格按照順序完成的。下面一起來看看吧!

Java類和物件的初始化順序

類裝載步驟

在Java中,類裝載器把一個類裝入Java虛擬機器中,要經過三個步驟來完成:裝載、連結和初始化,其中連結又可以分成校驗、準備和解析三步,除了解析外,其它步驟是嚴格按照順序完成的,各個步驟的主要工作如下:

裝載:查詢和匯入類或介面的二進位制資料;

連結:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;

校驗:檢查匯入類或介面的二進位制資料的正確性;

準備:給類的靜態變數分配並初始化儲存空間;

解析:將符號引用轉成直接引用;

初始化:啟用類的靜態變數的初始化Java程式碼和靜態Java程式碼塊。

其中 初始化(initialization)包含兩部分:

1.類的初始化(initialization class & interface)

2.物件的建立(creation of new class instances)。

因為類的初始化其實是類載入(loading of classes)的最後一步,所以很多書中把它歸結為“物件的建立”的第一步。其實只是看問題的角度不同而已。為了更清楚的理解,這裡還是分開來。

順序:

因為類的載入肯定是第一步的,所以類的初始化在前。大體的初始化順序是:

類初始化 -> 子類建構函式 -> 父類建構函式 -> 例項化成員變數 -> 繼續執行子類建構函式的語句

下面結合例子,具體解釋一下。

1. 類的初始化(Initialization classes and interfaces)

其實很簡單,具體來說有:

(a)初始化類(initialization of class),是指初始化static field 和執行static初始化塊。

public class Demo{ //初始化static field, //其中= "initialization static field"又叫做static field initializer private static String str = "initialization static field"; //初始化塊,又叫做static initializer,或 static initialization block static { tln("This is static initializer"); } }

btw,有些書上提到static initializer 和 static field initializer 的概念,與之對應的還有 instance initializer 和 instance variable initializer。例子中的`註釋已經解釋了其含義。

(b)初始化介面(initialization of interface),是指初始化定義在該interface中的field。

*注意*

1. initialization classes 時,該class的superclass 將首先被初始化,但其實現的interface則不會。

initialization classes 時,該class的superclass,以及superlcass的superclass 會首先被遞迴地初始化,一直到ct為止。但initialiazation interface的時候,卻不需如此,只會初始化該interface本身。

2. 對於由引用類變數(class field)所引發的初始化,只會初始化真正定義該field的class。

3. 如果一個static field是編譯時常量(compile-time constant),則對它的引用不會引起定義它的類的初始化。

為了幫助理解最後兩點,請試試看下面的例子:

Initialization類

public class Initialization { static { tln("Initialization Main class"); } public static void main(String[] args) { tln(Sub.y); tln(Sub.x); tln(Sub.z); } }

Sub類

public class Sub extends Super { public static final int y = 2005; public static int z; static { tln("Initialization Sub"); } }

Super類

public class Super { public static int x = 2006; static { tln("Initialization Super"); } }

輸入結果

Initialization Main class

2005

Initialization Super

2006

Initialization Sub

從這個結果可以看到,

static塊在類中會先執行;(實際上是先載入static成員變數,然後是static程式碼塊)

static 的final變數不會引起類的初始化;

子類Sub引用父類Super裡面的變數,就會引起父類的初始化,但不會引起子類的初始化;

static的成員變數也有預設值。

2. 物件的建立(creation of new class instances)

看例子來說明:

InitializationOrder類

public class InitializationOrder { public static void main(String[] args) { SubClass sb = new SubClass(); } }

SuperClass類

public class SuperClass{ static { tln("SuperClass static"); } SuperClass(String str){ tln(str); } }

Interface類

interface Interface{ static SuperClass su = new SuperClass("Interface new SuperClass"); }

SubClass類

public class SubClass extends SuperClass implements Interface{ static { tln("SubClass static"); } private SuperClass su = new SuperClass("initialization variable"); SubClass() { super("super"); new SuperClass("new SuperClass"); } }

輸出結果

SuperClass static

SubClass static

super

initialization variable

new SuperClass

解釋一下:

1) Java虛擬機器要執行InitializationOrder類中的static 方法main(),這引起了類的初始化。開始初始化InitializationOrder類。具體的步驟略去不說。

2) InitializationOrder類初始化完畢後,開始執行main()方法。語句SubClass sb = new SubClass()將建立一個SubClass物件。載入類SubClass後對其進行類初始化,因為Subclass有一個父類SuperClass,所以先初始化SuperClass類。於是看到輸出“SuperClass static”。

3) SuperClass類初始化完畢後,開始初始化SubClass類,輸出“SubClass static”。

4) 至此,類的載入工作全部完成。開始進入建立SubClass的物件過程。先為SubClass類和其父類SuperClass類分配記憶體空間,這時Super su 被賦值為null。

5) 執行建構函式SubClass(),執行super(), 呼叫父類的建構函式,輸出“super”。

6) 初始化SubClass類的成員變數su,輸出“initialization variable”。

7) 繼續執行建構函式的剩餘部分,執行new SuperClass("new SuperClass"),輸出“new SuperClass”,這時Super su 被賦值新建物件的引用。

8) 而SubClass雖然實現了介面Interface,但是初始化它的時候並不會引起介面的初始化,所以介面Interface中的static SuperClass su = new SuperClass("Interface new SuperClass")自始至終都沒有被執行到。

所以物件的建立,具體步驟如下:

(1) 所有的成員變數—包括該類,及它的父類中的成員變數--被分配記憶體空間,並賦予預設值。(這裡是第一次初始化成員變數)

(2) 為所呼叫的建構函式初始化其引數變數。(如果有引數)

(3) 如果在建構函式中用this 呼叫了同類中的其他建構函式,則按照步驟(2)~(6)去處理被呼叫到的建構函式。

(4) 如果在建構函式中用super呼叫了其父類的建構函式,則按照步驟(2)~(6)去處理被呼叫到的父類建構函式。

(5) 按照書寫順序,執行instance initializer 和 instance variable initializer來初始化成員變數。(這裡是第二次初始化成員變數)

(6) 按照書寫順序,執行建構函式的其餘部分。

總結:

從類的初始化和物件的建立步驟,可以知道,一個類是先初始化static的變數和static句塊,然後在分配該類以及父類的成員變數的記憶體空間,賦予預設值,然後開始呼叫建構函式。而子類和父類之間,則先初始化和建立父類,然後在初始化和建立子類的。