0.基本概念java
類變量是指java類中的static數據成員,實例變量則是指java類中的非static數據成員。因爲類變量不須要建立一個對象便可訪問,而實例變量則必須與一個具體的對象對應,所以類變量和實例變量的初始化時機是不一樣的。本文主要關注如下3個問題:函數
(1)何時初始化類變量?何時初始化實例變量?spa
(2)何時會同時初始化類變量和實例變量?code
(3)在(1)、(2)情形下各個類變量和實例變量的初始化順序?對象
1.類變量初始化blog
1.1 類變量初始化方式繼承
static變量初始化的方式有兩種:定義時初始化和static代碼塊中初始化。以下代碼所示:ci
1 class A(){ 2 public static int i = 1;//static變量初始化方式一:在定義時初始化 3 static{//static變量初始化方式二:在static代碼塊初始化static變量 4 System.out.println("statci 代碼塊"); 5 } 6 }
1.2 類變量初始化時機編譯
根據有無被final關鍵字修飾,類變量的初始化時機不一樣。被final關鍵字修飾的類變量在編譯時就已經被初始化被放置在常量池中了,而沒有被final修飾的類變量則在該類首次使用時被初始化。特別須要注意的是,類變量只在該類首次使用時被初始化,這意味着類變量只會被初始化一次。class
在下列情形下,若是一個java類中沒有被final修飾的類變量還沒有初始化,那麼這些沒有被final修飾的類變量將會被初始化:
(1)訪問該類中沒有被final修飾的類變量時;
(2)設置該類中沒有被final修飾的類變量時;
(3)調用該類的static方法。
1.2 類變量初始化順序
類變量初始化會遵循如下三個原則:
(1)在上述3個情形下,首先會初始化該類中全部沒有被final修飾的類變量,而後再執行對應的操做;
(2)按照代碼中的順序依次執行static變量定義語句和static代碼塊;
(3)若是該類有父類且父類的類變量沒有初始化,則先初始化父類的類變量;
(4)若是父類還有父類的話,根據(3)的規則,以此類推。
根據上述原則,能夠肯定類變量初始化順序以下:
//沒有繼承的情形 static變量初始化和static代碼塊 //有繼承的情形 1.父類的static變量初始化和static代碼塊 2.子類的static變量初始化和static代碼塊 /*備註: 類變量初始化只有一次,若是某個java類的類變量已經被初始化過了,則上述過程當中的類變量初始化將再也不執行 */
2.實例變量初始化
2.1 實例變量初始化方式
與類變量初始化方式相似,實例變量初始化方式也有兩種:定義時初始化和代碼塊初始化。示例以下:
1 class A(){ 2 public static int i = 1;//實例變量初始化方式一:在定義時初始化 3 4 {//實例變量初始化方式二:在代碼塊初始化實例變量 5 System.out.println("代碼塊"); 6 } 7 }
2.2 實例變量初始化時機和順序
實例變量初始化是在建立該類的對象時進行的,初始化時遵循的原則是:
(1)按照代碼中的順序依次執行實例變量定義語句和實例變量代碼塊;
(2)若是建立該類的對象時該類的類變量還沒有初始化,則先初始化類變量,再初始化實例變量;
(3)若是該類有父類的話,則先建立一個父類對象;而且,若是父類類變量沒被初始化時,先初始化父類的類變量,再初始化父類的實例變量,再調用父類的默認構造器;
(4)若是父類還有父類的話,根據(3)的規則以此類推,一直到根基類。
根據上述原則易知,實例變量初始化以前可能會先初始化類變量(這是特別須要留意的一點)。初始化順序以下:
//沒有繼承的情形(且該類的類變量未被初始化) 1.static變量初始化和static代碼塊 2.實例變量初始化和實例變量初始化代碼塊 3.構造函數 //有繼承的情形(且該類和父類的類變量未被初始化) 1.父類的static變量初始化和static代碼塊 2.子類的static變量初始化和static代碼塊 3.父類的實例變量初始化和實例變量初始化代碼塊 4.父類的構造函數 5.子類的實例變量初始化和實例變量初始化代碼塊 6.子類構造函數 /*備註: 類變量初始化只有一次,若是某個java類的類變量已經被初始化過了,則上述過程當中的類變量初始化將再也不執行 */
3.小結
你們看完若是還不太明白類變量和實例變量的初始化時機和順序,能夠親自去寫一個demo試試,實踐才能出真知嘛!
在這裏,只強調兩點:
(1)類變量只會初始化一次;
(2)類變量和實例變量的初始化時機是不一樣的,類變量初始化在首次使用該類的時候進行,實例變量的初始化在建立該類的每一個對象時都會進行。