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,然后執行非靜態代碼塊,最后執行構造方法里面的代碼。
*在存在父類的時候,調用子類的構造時,會先調用父類的默認構造(空參構造),進行父類的初始化。