Java中的類初始化解析
類的初始化
類的初始化是一個Java類生命周期中的其中一個階段。
如下圖所示:

生命周期中的前五個階段(加載、驗證、準備、解析、初始化)是一個類在JVM中的完整加載過程。
初始化是類加載的最后一個階段,也正是在初始化階段,才會真正開始執(zhí)行類中所寫的Java代碼。
Java虛擬機規(guī)范中嚴格規(guī)定了有且只有四種情況必須立即對類進行初始化:
1.遇到new、getstatic、putstatic、或者invoicestatic這4條字節(jié)碼指令時,如果類沒有進行過初始化,則需要觸發(fā)其初始化。生成這4條指令的最常見的Java代碼場景是:使用new關(guān)鍵字實例化對象、讀取或設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯期就把結(jié)果放入常量池的靜態(tài)字段除外)的時候,以及調(diào)用一個類的靜態(tài)方法的時候。
2.使用java.lang.reflect包的方法對類進行反射調(diào)用的時候,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。
3.當初始化一個類的時候,如果發(fā)現(xiàn)父類還沒有進行過初始化,需要先觸發(fā)其父類的初始化。
4.當虛擬機啟動時,用戶需要指定一個主類(包含main方法的那個類),虛擬機會先初始化這個主類。
這四種場景中的行為稱為對一個類進行主動引用,除此之外引用類的方式,都不會觸發(fā)初始化,被稱為被動引用。
初始化一個類時,最先執(zhí)行的是靜態(tài)域中代碼,而靜態(tài)域中包含靜態(tài)變量、靜態(tài)塊和靜態(tài)方法,其中需要初始化的是靜態(tài)變量和靜態(tài)塊,這兩者的執(zhí)行順序由代碼的書寫順序決定。
靜態(tài)域中的代碼只會被執(zhí)行一次。如果是使用new關(guān)鍵字實例化對象,靜態(tài)域中的代碼被執(zhí)行后,緊接著會執(zhí)行構(gòu)造代碼塊和構(gòu)造函數(shù)來實例化對象,這部分代碼會執(zhí)行多次,每次使用new關(guān)鍵字實例化對象時,這部分代碼都會執(zhí)行??梢杂萌缦乱欢纬绦蝌炞C這些代碼的執(zhí)行順序:
public class ClassInitTest {
public static ClassInitTest c1 = new ClassInitTest();
public static ClassInitTest c2 = new ClassInitTest();
{
System.out.println("構(gòu)造代碼塊執(zhí)行");
}
static {
System.out.println("靜態(tài)代碼塊執(zhí)行");
}
public ClassInitTest(){
System.out.println("構(gòu)造方法被執(zhí)行");
}
public static void main(String[] args){
System.out.println("main方法的第一行被執(zhí)行");
ClassInitTest c = new ClassInitTest();
System.out.println("main方法的第二行被執(zhí)行");
}
}ClassInitTest是主類,當JVM啟動時會對該類進行初始化,首先初始化靜態(tài)域中的代碼,可以看到首先是兩個靜態(tài)變量c1和c2,對c1、c2聲明的同時進行變量值的初始化,其值是實例對象,因此構(gòu)造代碼塊和構(gòu)造方法會被執(zhí)行,new了兩次,因此會被執(zhí)行兩次。
緊接著會執(zhí)行靜態(tài)代碼塊中的代碼。靜態(tài)域中的代碼執(zhí)行完畢后便開始執(zhí)行main方法,先是輸出一行語句,接著又實例化了一個對象,構(gòu)造代碼塊和構(gòu)造方法會被執(zhí)行,最后又輸出一行語句。因此執(zhí)行的結(jié)果如下:

如果存在繼承的情況,初始化子類時,會先初始化父類,同時這幾類代碼執(zhí)行順序的優(yōu)先級保持不變。實例代碼如下:
public class Father {
static {
System.out.println("父類的靜態(tài)代碼塊被執(zhí)行");
}
{
System.out.println("父類的構(gòu)造代碼塊被執(zhí)行");
}
public Father(){
System.out.println("父類的構(gòu)造方法被執(zhí)行");
}
}
public class Child extends Father{
public Child(){
System.out.println("子類構(gòu)造方法被執(zhí)行");
}
{
System.out.println("子類的構(gòu)造代碼塊被執(zhí)行");
}
static {
System.out.println("子類的靜態(tài)代碼塊被執(zhí)行");
}
public static void main(String [] args){
new Child();
}
}執(zhí)行結(jié)果如下:

之所以子類的構(gòu)造方法執(zhí)行前會先執(zhí)行父類的構(gòu)造方法,是因為子類的構(gòu)造方法總是先調(diào)用父類的某個構(gòu)造方法,也就是說,如果子類沒有明顯地指明使用父類的哪個構(gòu)造方法,子類就調(diào)用父類不帶參數(shù)的構(gòu)造方法。
子類中使用super關(guān)鍵字調(diào)用父類的構(gòu)造方法,而且super必須是子類構(gòu)造方法的第一條語句,如果子類構(gòu)造方法中沒有明顯地寫出super關(guān)鍵字調(diào)用父類的哪個構(gòu)造方法,那么默認在子類構(gòu)造方法中有如下語句(即使不寫):
super();//調(diào)用父類的無參構(gòu)造函數(shù)
當然也可以在子類的構(gòu)造方法中顯式地用super語句指明調(diào)用父類的哪個構(gòu)造方法(如果父類有多個構(gòu)造方法的話)
super(實參列表);
到此這篇關(guān)于Java中的類初始化解析的文章就介紹到這了,更多相關(guān)Java類初始化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java開發(fā)或調(diào)用WebService的幾種方式總結(jié)
java開發(fā)過程中,很多地方都會遇到數(shù)據(jù)傳遞,遠程獲取數(shù)據(jù)問題,這篇文章主要介紹了Java開發(fā)或調(diào)用WebService的幾種方式的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-06-06
java對xml節(jié)點屬性的增刪改查實現(xiàn)方法
下面小編就為大家?guī)硪黄猨ava對xml節(jié)點屬性的增刪改查實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10
Java中的FileWriter用法詳解與實戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于Java中FileWriter用法的相關(guān)資料,包括寫入字符數(shù)據(jù)到文件、字符數(shù)組和部分字符寫入、配合BufferedWriter使用等方法,同時也解釋了其與OutputStreamWriter,BufferedWriter的異同特性,適合簡單的文件寫入操作,需要的朋友可以參考下2024-10-10
Spring Boot 3.x 全新的熱部署配置方式詳解(IntelliJ ID
這篇文章主要介紹了Spring Boot 3.x 全新的熱部署配置方式(IntelliJ IDEA 2023.1),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
SpringBoot整合WebSocket的客戶端和服務(wù)端的實現(xiàn)代碼
這篇文章主要介紹了SpringBoot整合WebSocket的客戶端和服務(wù)端的實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07
淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型
這篇文章主要介紹了淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型,具有一定借鑒價值,需要的朋友可以參考下2017-12-12
Maven編譯時缺少依賴,java:程序包org.apache.http不存在的問題
這篇文章主要介紹了Maven編譯時缺少依賴,java:程序包org.apache.http不存在的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05

