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句塊,然後在分配該類以及父類的成員變量的內存空間,賦予默認值,然後開始調用構造函數。而子類和父類之間,則先初始化和創建父類,然後在初始化和創建子類的。
-
Java開發崗位面試題
為幫助參加Java開發崗位面試的朋友們做好準備,以下是本站小編搜索整理的一份Java集合面試問題40個【附答案】,供參考練習,希望對大家有所幫助!想了解更多相關信息請持續關注我們應屆畢業生考試網!一、Java基礎1、String類為什麼是final的。2、HashMap的源碼,實現原...
-
計算機二級JAVA考試構建JAVA程序2017
很多人一見到説要編程就以為很難,其實只有一點一滴學知識點也不難,下面是小編整理的計算機二級JAVA考試構建JAVA程序介紹,歡迎閲讀!第一個javaapplicationjava程序分為javaapplication(java應用程序)和javaapplet(java小應用程序)兩種。下面讓我們編寫一個java應...
-
java考試複習題
人類的希望像是一顆永恆的星,烏雲掩不住它的光芒。特別是在今天,和平不是一個理想,一個夢,它是萬人的願望。以下是小編為大家搜索整理的java考試複習題,希望能給大家帶來幫助!更多精彩內容請及時關注我們應屆畢業生考試網!一、選擇題1、以下程序段執行後的K值為()。...
-
springmvc如何設置多視圖器呢
導語:springmvc如何設置多視圖器呢?下面是小編給大家整體的代碼,大家可以參考練習,更多詳情請關注應屆畢業生考試網。在做頁面靜態化處理時,有時候我們需要兩種或者兩種以上的視圖解析方式,比如jsp,html,json,jstl,ftl等等,顯然默認的springmvc只配置一種視圖解析方...