java中類初始化時機
java虛擬機器規範雖然沒有強制性約束在什麼時候開始類載入過程,但是對於類的初始化,虛擬機器規範則嚴格規定了有且只有四種情況必須立即對類進行初始化,遇到new、getStatic、putStatic或invokeStatic這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。
生成這4條指令最常見的java程式碼場景是:
1)使用new關鍵字例項化物件
2)讀取一個類的靜態欄位(被final修飾、已在編譯期把結果放在常量池的靜態欄位除外)
3)設定一個類的靜態欄位(被final修飾、已在編譯期把結果放在常量池的靜態欄位除外)
4)呼叫一個類的靜態方法
驗證:
1)當類被初始化時,其靜態程式碼塊會執行。
class ClassLoadTime{
static{
System.out.println("ClassLoadTime類初始化時就會被執行!");
}
public ClassLoadTime(){
System.out.println("ClassLoadTime建構函式!");
}
}
class ClassLoadDemo{
public static void main(String[] args){
ClassLoadTime clt = new ClassLoadTime();
}
}
輸出結果:
ClassLoadTime類初始化時就會被執行!
ClassLoadTime建構函式!
2) 讀取一個類的靜態欄位(被final修飾、已在編譯期把結果放在常量池的靜態欄位除外)
class ClassLoadTime{
static{
System.out.println("ClassLoadTime類初始化時就會被執行!");
}
public static int max = 200; (防止測試類和此類不在一個包,使用public修飾符)
public ClassLoadTime(){
System.out.println("ClassLoadTime建構函式!");
}
}
class ClassLoadDemo{
public static void main(String[] args){
int value = ClassLoadTime.max;
System.out.println(value);
}
}
輸出:
ClassLoadTime類初始化時就會被執行!
200
3)設定一個類的靜態欄位(被final修飾、已在編譯期把結果放在常量池的靜態欄位除外)
class ClassLoadTime{
static{
System.out.println("ClassLoadTime類初始化時就會被執行!");
}
public static int max = 200; (防止測試類和此類不在一個包,使用public修飾符)
public ClassLoadTime(){
System.out.println("ClassLoadTime建構函式!");
}
}
class ClassLoadDemo{
public static void main(String[] args){
ClassLoadTime.max = 100;
}
}
輸出:
ClassLoadTime類初始化時就會被執行!
4)呼叫一個類的靜態方法
class ClassLoadTime{
static{
System.out.println("ClassLoadTime類初始化時就會被執行!");
}
public static int max = 200; (防止測試類和此類不在一個包,使用public修飾符)
public ClassLoadTime(){
System.out.println("ClassLoadTime建構函式!");
}
public static void method(){
System.out.println("靜態方法的呼叫!");
}
}
class ClassLoadDemo{
public static void main(String[] args){
ClassLoadTime.method();
}
}
輸出:
ClassLoadTime類初始化時就會被執行!
靜態方法的呼叫!
被final修飾靜態欄位在操作使用時,不會使類進行初始化,因為在編譯期已經將此常量放在常量池。
測試:
class ClassLoadTime{
static{
System.out.println("ClassLoadTime類初始化時就會被執行!");
}
public static final int MIN = 10; (防止測試類和此類不在一個包,使用public修飾符)
}
class ClassLoadDemo{
public static void main(String[] args){
System.out.println(ClassLoadTime.MIN);
}
}
輸出:
10
子類呼叫或者設定父類的靜態欄位或者呼叫父類的靜態方法時僅僅初始化父類,而不初始化子類。同樣讀取final修飾的常量不會進行類的初始化。
class Fu{
public static int value = 20;
static{
System.out.println("父類進行了類的初始化!");
}
}
class Zi{
static{
System.out.println("子類進行了類的初始化!");
}
}
class LoadDemo{
public static void main(String[] args){
System.out.println(Zi.value);
}
}
輸出:
父類進行了類的初始化!
20
java類中各種成員的初始化時機,此處不一一測試:
類變數(靜態變數)、例項變數(非靜態變數)、靜態程式碼塊、非靜態程式碼塊 的初始化時機:
* 由 static 關鍵字修飾的(如:類變數[靜態變數]、靜態程式碼塊)將在類被初始化建立例項物件之前被初始化,而且是按順序從上到下依次被執行;
public static int value =34;
static{
System.out.println("靜態程式碼塊!");
}
public 類名(){
System.out.println("建構函式!");
}
一旦這樣寫,在類被初始化建立例項物件之前會先初始化靜態欄位value,然後執行靜態程式碼塊,當例項化物件時會執行構造方法中的程式碼
* 沒有 static 關鍵字修飾的(如:例項變數[非靜態變數]、非靜態程式碼塊)初始化實際上是會被提取到類的構造器中被執行的,但是會比類構造器中的
程式碼塊優先執行到,其也是按順序從上到下依次被執行。
public int value =34;
{
System.out.println("非靜態程式碼塊!");
}
public 類名(){
System.out.println("建構函式!");
}
在使用建構函式例項化一個物件時,會先初始化value,然後執行非靜態程式碼塊,最後執行構造方法裡面的程式碼。
*在存在父類的時候,呼叫子類的構造時,會先呼叫父類的預設構造(空參構造),進行父類的初始化。