java中類加載和類初始化順序的示例分析-創(chuàng)新互聯(lián)

小編給大家分享一下java中類加載和類初始化順序的示例分析,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比通州網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式通州網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋通州地區(qū)。費(fèi)用合理售后完善,十年實(shí)體公司更值得信賴。

1.對(duì)于靜態(tài)變量,靜態(tài)代碼塊,變量,初始化代碼塊,構(gòu)造器,他們的依次順序?yàn)?,(靜態(tài)變量,靜態(tài)代碼塊)>(變量,初始化代碼塊)>構(gòu)造器

存在子類和父類的情況如下:

(1)單個(gè)類的執(zhí)行情況,代碼如下:
public class InitOrderTest {
// 靜態(tài)變量

public static String staticField = "靜態(tài)變量";

// 變量
public String field = "變量";

// 靜態(tài)初始化塊
static {
    System.out.println(staticField);
    System.out.println("靜態(tài)初始化塊");
}

// 初始化塊
{
    System.out.println(field);
    System.out.println("初始化塊");
}

// 構(gòu)造器
public InitOrderTest() {
    System.out.println("構(gòu)造器");
}

public static void main(String[] args) {
    new InitOrderTest();
}

}
輸出如下:

(2)對(duì)于繼承的情況如下:

class Parent {  
// 靜態(tài)變量
public static String p_StaticField = "父類--靜態(tài)變量";
protected int i = 1;
protected int j = 8;
// 變量
public String p_Field = "父類--變量";

// 靜態(tài)初始化塊 
static {   
    System.out.println(p_StaticField);   
    System.out.println("父類--靜態(tài)初始化塊");   
}   

// 初始化塊 
{   
    System.out.println(p_Field);   
    System.out.println("父類--初始化塊");   
}   

// 構(gòu)造器 
public Parent() {   
    System.out.println("父類--構(gòu)造器"); 
    System.out.println("i=" + i + ", j=" + j);
    j = 9;
}

}

public class SubClass extends Parent {

// 靜態(tài)變量 
public static String s_StaticField = "子類--靜態(tài)變量";

// 變量 
public String s_Field = "子類--變量";   

// 靜態(tài)初始化塊 
static {   
    System.out.println(s_StaticField);   
    System.out.println("子類--靜態(tài)初始化塊");   
}   
// 初始化塊 
{   
    System.out.println(s_Field);   
    System.out.println("子類--初始化塊");   
}   

// 構(gòu)造器 
public SubClass() {   
    System.out.println("子類--構(gòu)造器"); 
    System.out.println("i=" + i + ",j=" + j);
}   

// 程序入口 
public static void main(String[] args) {
    new SubClass();   
}

}
輸出如下:

現(xiàn)在,結(jié)果已經(jīng)不言自明了。子類的靜態(tài)變量和靜態(tài)初始化塊的初始化是在父類的變量、初始化塊和構(gòu)造器初始化之前就完成了。
靜態(tài)變量、靜態(tài)初始化塊,變量、初始化塊初始化了順序取決于它們?cè)陬愔谐霈F(xiàn)的先后順序。
執(zhí)行過程分析
 (1)訪問SubClass.main(),(這是一個(gè)static方法),于是裝載器就會(huì)為你尋找已經(jīng)編譯的SubClass類的代碼(也就是SubClass.class文件)。在裝載的過程中,裝載器注意到它有一個(gè)基類(也就是extends所要表示的意思),于是它再裝載基類。不管你創(chuàng)不創(chuàng)建基類對(duì)象,這個(gè)過程總會(huì)發(fā)生。如果基類還有基類,那么第二個(gè)基類也會(huì)被裝載,依此類推。

(2)執(zhí)行根基類的static初始化,然后是下一個(gè)派生類的static初始化,依此類推。這個(gè)順序非常重要,因?yàn)榕缮惖摹皊tatic初始化”有可能要依賴基類成員的正確初始化。

(3)當(dāng)所有必要的類都已經(jīng)裝載結(jié)束,開始執(zhí)行main()方法體,并用new SubClass()創(chuàng)建對(duì)象。

(4)類SubClass存在父類,則調(diào)用父類的構(gòu)造函數(shù),你可以使用super來指定調(diào)用哪個(gè)構(gòu)造函數(shù)(也就是Beetle()構(gòu)造函數(shù)所做的第一件事)。

基類的構(gòu)造過程以及構(gòu)造順序,同派生類的相同。首先基類中各個(gè)變量按照字面順序進(jìn)行初始化,然后執(zhí)行基類的構(gòu)造函數(shù)的其余部分。

(5)對(duì)子類成員數(shù)據(jù)按照它們聲明的順序初始化,執(zhí)行子類構(gòu)造函數(shù)的其余部分。

接下來分析java的類加載機(jī)制,在分析類加載機(jī)制之前,請(qǐng)看下面一道題:
class Singleton {
private static Singleton singleton = new Singleton();
public static int value1;
public static int value2 = 0;

private Singleton() {
    value1++;
    value2++;
}

public static Singleton getInstance() {
    return singleton;
}

}

class Singleton2 {
public static int value1;
public static int value2 = 0;
private static Singleton2 singleton2 = new Singleton2();

private Singleton2() {
    value1++;
    value2++;
}

public static Singleton2 getInstance2() {
    return singleton2;
}

}

public class TestClassLoader {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("Singleton1 value1:" + singleton.value1);
System.out.println("Singleton1 value2:" + singleton.value2);

    Singleton2 singleton2 = Singleton2.getInstance2();
    System.out.println("Singleton2 value1:" + singleton2.value1);
    System.out.println("Singleton2 value2:" + singleton2.value2);
}

}
輸出結(jié)果如下:

jvm類加載分為5個(gè)過程:加載,驗(yàn)證,準(zhǔn)備,解析,初始化,使用,卸載,如下所示:

下面來看看加載,驗(yàn)證,準(zhǔn)備,解析,初始化這5個(gè)過程的具體動(dòng)作。

1.1 加載
加載主要是將.class文件(并不一定是.class??梢允荶IP包,網(wǎng)絡(luò)中獲?。┲械亩M(jìn)制字節(jié)流讀入到JVM中。
在加載階段,JVM需要完成3件事:
1)通過類的全限定名獲取該類的二進(jìn)制字節(jié)流;
2)將字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);
3)在內(nèi)存中生成一個(gè)該類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。

1.2 連接
1.2.1 驗(yàn)證
驗(yàn)證是連接階段的第一步,主要確保加載進(jìn)來的字節(jié)流符合JVM規(guī)范。
驗(yàn)證階段會(huì)完成以下4個(gè)階段的檢驗(yàn)動(dòng)作:
1)文件格式驗(yàn)證
2)元數(shù)據(jù)驗(yàn)證(是否符合Java語(yǔ)言規(guī)范)
3)字節(jié)碼驗(yàn)證(確定程序語(yǔ)義合法,符合邏輯)
4)符號(hào)引用驗(yàn)證(確保下一步的解析能正常執(zhí)行)

1.2.2 準(zhǔn)備
準(zhǔn)備是連接階段的第二步,主要為靜態(tài)變量在方法區(qū)分配內(nèi)存,并設(shè)置默認(rèn)初始值。

1.2.3 解析
解析是連接階段的第三步,是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過程。

1.3 初始化
初始化階段是類加載過程的最后一步,主要是根據(jù)程序中的賦值語(yǔ)句主動(dòng)為類變量賦值。
注:
1)當(dāng)有父類且父類為初始化的時(shí)候,先去初始化父類;
2)再進(jìn)行子類初始化語(yǔ)句。

什么時(shí)候需要對(duì)類進(jìn)行初始化?
1)使用new該類實(shí)例化對(duì)象的時(shí)候;
2)讀取或設(shè)置類靜態(tài)字段的時(shí)候(但被final修飾的字段,在編譯器時(shí)就被放入常量池的靜態(tài)字段除外static final);
3)調(diào)用類靜態(tài)方法的時(shí)候;
4)使用反射Class.forName(“xxxx”)對(duì)類進(jìn)行反射調(diào)用的時(shí)候,該類需要初始化;
5) 初始化一個(gè)類的時(shí)候,有父類,先初始化父類(注:1. 接口除外,父接口在調(diào)用的時(shí)候才會(huì)被初始化;2.子類引用父類靜態(tài)字段,只會(huì)引發(fā)父類初始化);
6) 被標(biāo)明為啟動(dòng)類的類(即包含main()方法的類)要初始化;
7)當(dāng)使用JDK1.7的動(dòng)態(tài)語(yǔ)言支持時(shí),如果一個(gè)java.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個(gè)方法句柄所對(duì)應(yīng)的類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。

以上情況稱為對(duì)一個(gè)類進(jìn)行主動(dòng)引用,且有且只要以上幾種情況需要對(duì)類進(jìn)行初始化。
再回過頭來分析一開始的面試題:
Singleton輸出結(jié)果:1 0
原因:

1 首先執(zhí)行main中的Singleton singleton = Singleton.getInstance();
2 類的加載:加載類Singleton
3 類的驗(yàn)證
4 類的準(zhǔn)備:為靜態(tài)變量分配內(nèi)存,設(shè)置默認(rèn)值。這里為singleton(引用類型)設(shè)置為null,value1,value2(基本數(shù)據(jù)類型)設(shè)置默認(rèn)值0
5 類的初始化(按照賦值語(yǔ)句進(jìn)行修改):
執(zhí)行private static Singleton singleton = new Singleton();
執(zhí)行Singleton的構(gòu)造器:value1++;value2++; 此時(shí)value1,value2均等于1
執(zhí)行
public static int value1;
public static int value2 = 0;
此時(shí)value1=1,value2=0

Singleton2輸出結(jié)果:1 1
原因:

1 首先執(zhí)行main中的Singleton2 singleton2 = Singleton2.getInstance2();
2 類的加載:加載類Singleton2
3 類的驗(yàn)證
4 類的準(zhǔn)備:為靜態(tài)變量分配內(nèi)存,設(shè)置默認(rèn)值。這里為value1,value2(基本數(shù)據(jù)類型)設(shè)置默認(rèn)值0,singleton2(引用類型)設(shè)置為null,
5 類的初始化(按照賦值語(yǔ)句進(jìn)行修改):
執(zhí)行
public static int value2 = 0;
此時(shí)value2=0(value1不變,依然是0);
執(zhí)行
private static Singleton singleton = new Singleton();
執(zhí)行Singleton2的構(gòu)造器:value1++;value2++;
此時(shí)value1,value2均等于1,即為最后結(jié)果

類加載器之間的層次關(guān)系如下:

類加載器之間的這種層次關(guān)系叫做雙親委派模型。
雙親委派模型要求除了頂層的啟動(dòng)類加載器(Bootstrap ClassLoader)外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。這里的類加載器之間的父子關(guān)系一般不是以繼承關(guān)系實(shí)現(xiàn)的,而是用組合實(shí)現(xiàn)的。

雙親委派模型的工作過程
如果一個(gè)類接受到類加載請(qǐng)求,他自己不會(huì)去加載這個(gè)請(qǐng)求,而是將這個(gè)類加載請(qǐng)求委派給父類加載器,這樣一層一層傳送,直到到達(dá)啟動(dòng)類加載器(Bootstrap ClassLoader)。
只有當(dāng)父類加載器無(wú)法加載這個(gè)請(qǐng)求時(shí),子加載器才會(huì)嘗試自己去加載。

雙親委派模型的代碼實(shí)現(xiàn)
雙親委派模型的代碼實(shí)現(xiàn)集中在java.lang.ClassLoader的loadClass()方法當(dāng)中。
1)首先檢查類是否被加載,沒有則調(diào)用父類加載器的loadClass()方法;
2)若父類加載器為空,則默認(rèn)使用啟動(dòng)類加載器作為父加載器;
3)若父類加載失敗,拋出ClassNotFoundException 異常后,再調(diào)用自己的findClass() 方法。

loadClass源代碼如下:

破壞雙親委派模型
雙親委派模型很好的解決了各個(gè)類加載器加載基礎(chǔ)類的統(tǒng)一性問題。即越基礎(chǔ)的類由越上層的加載器進(jìn)行加載。
若加載的基礎(chǔ)類中需要回調(diào)用戶代碼,而這時(shí)頂層的類加載器無(wú)法識(shí)別這些用戶代碼,怎么辦呢?這時(shí)就需要破壞雙親委派模型了。
下面介紹兩個(gè)例子來講解破壞雙親委派模型的過程。

JNDI破壞雙親委派模型
JNDI是Java標(biāo)準(zhǔn)服務(wù),它的代碼由啟動(dòng)類加載器去加載。但是JNDI需要回調(diào)獨(dú)立廠商實(shí)現(xiàn)的代碼,而類加載器無(wú)法識(shí)別這些回調(diào)代碼(SPI)。
為了解決這個(gè)問題,引入了一個(gè)線程上下文類加載器。 可通過Thread.setContextClassLoader()設(shè)置。
利用線程上下文類加載器去加載所需要的SPI代碼,即父類加載器請(qǐng)求子類加載器去完成類加載的過程,而破壞了雙親委派模型。

Spring破壞雙親委派模型
Spring要對(duì)用戶程序進(jìn)行組織和管理,而用戶程序一般放在WEB-INF目錄下,由WebAppClassLoader類加載器加載,而Spring由Common類加載器或Shared類加載器加載。
那么Spring是如何訪問WEB-INF下的用戶程序呢?
使用線程上下文類加載器。 Spring加載類所用的classLoader都是通過Thread.currentThread().getContextClassLoader()獲取的。當(dāng)線程創(chuàng)建時(shí)會(huì)默認(rèn)創(chuàng)建一個(gè)AppClassLoader類加載器(對(duì)應(yīng)Tomcat中的WebAppclassLoader類加載器): setContextClassLoader(AppClassLoader)。
利用這個(gè)來加載用戶程序。即任何一個(gè)線程都可通過getContextClassLoader()獲取到WebAppclassLoader。

看完了這篇文章,相信你對(duì)“java中類加載和類初始化順序的示例分析”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站制作公司行業(yè)資訊頻道,感謝各位的閱讀!

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。

新聞名稱:java中類加載和類初始化順序的示例分析-創(chuàng)新互聯(lián)
當(dāng)前地址:http://www.muchs.cn/article24/coisje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)網(wǎng)站制作、用戶體驗(yàn)網(wǎng)頁(yè)設(shè)計(jì)公司、網(wǎng)站收錄、App開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營(yíng)