欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java類(lèi)的加載時(shí)機(jī)與過(guò)程

 更新時(shí)間:2021年12月12日 16:37:44   作者:Boblim  
這篇文章主要介紹了Java類(lèi)的加載時(shí)機(jī)與過(guò)程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

1 開(kāi)門(mén)見(jiàn)山

以前曾經(jīng)看到過(guò)一個(gè)java的面試題,當(dāng)時(shí)覺(jué)得此題很簡(jiǎn)單,可是自己把代碼運(yùn)行起來(lái),可是結(jié)果并不是自己想象的那樣。題目如下:

class SingleTon {
    private static SingleTon singleTon = new SingleTon();
    public static int count1;
    public static int count2 = 0;
 
    private SingleTon() {
        count1++;
        count2++;
    }
 
    public static SingleTon getInstance() {
        return singleTon;
    }
}
 
public class Test {
    public static void main(String[] args) {
        SingleTon singleTon = SingleTon.getInstance();
        System.out.println("count1=" + singleTon.count1);
        System.out.println("count2=" + singleTon.count2);
    }
}

錯(cuò)誤答案

count1=1

count2=1

正確答案

count1=1

count2=0

為神馬?為神馬?這要從java的類(lèi)加載時(shí)機(jī)說(shuō)起。

2 類(lèi)的加載時(shí)機(jī)

類(lèi)從被加載到虛擬機(jī)內(nèi)存中開(kāi)始,直到卸載出內(nèi)存為止,它的整個(gè)生命周期包括了:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用和卸載這7個(gè)階段。其中,驗(yàn)證、準(zhǔn)備和解析這三個(gè)部分統(tǒng)稱為連接(linking)。

其中,加載、驗(yàn)證、準(zhǔn)備、初始化和卸載這五個(gè)階段的順序是確定的,類(lèi)的加載過(guò)程必須按照這種順序按部就班的“開(kāi)始”(僅僅指的是開(kāi)始,而非執(zhí)行或者結(jié)束,因?yàn)檫@些階段通常都是互相交叉的混合進(jìn)行,通常會(huì)在一個(gè)階段執(zhí)行的過(guò)程中調(diào)用或者激活另一個(gè)階段),而解析階段則不一定(它在某些情況下可以在初始化階段之后再開(kāi)始,這是為了支持Java語(yǔ)言的運(yùn)行時(shí)綁定。

3 何時(shí)開(kāi)始類(lèi)的初始化

什么情況下需要開(kāi)始類(lèi)加載過(guò)程的第一個(gè)階段:"加載"。虛擬機(jī)規(guī)范中并沒(méi)強(qiáng)行約束,這點(diǎn)可以交給虛擬機(jī)的的具體實(shí)現(xiàn)自由把握,但是對(duì)于初始化階段虛擬機(jī)規(guī)范是嚴(yán)格規(guī)定了如下幾種情況,如果類(lèi)未初始化會(huì)對(duì)類(lèi)進(jìn)行初始化。

  1. 創(chuàng)建類(lèi)的實(shí)例
  2. 訪問(wèn)類(lèi)的靜態(tài)變量(除常量【被final修辭的靜態(tài)變量】原因:常量一種特殊的變量,因?yàn)榫幾g器把他們當(dāng)作值(value)而不是域(field)來(lái)對(duì)待。如果你的代碼中用到了常變量(constant variable),編譯器并不會(huì)生成字節(jié)碼來(lái)從對(duì)象中載入域的值,而是直接把這個(gè)值插入到字節(jié)碼中。這是一種很有用的優(yōu)化,但是如果你需要改變final域的值那么每一塊用到那個(gè)域的代碼都需要重新編譯。
  3. 訪問(wèn)類(lèi)的靜態(tài)方法
  4. 反射如(Class.forName("my.xyz.Test"))
  5. 當(dāng)初始化一個(gè)類(lèi)時(shí),發(fā)現(xiàn)其父類(lèi)還未初始化,則先出發(fā)父類(lèi)的初始化
  6. 虛擬機(jī)啟動(dòng)時(shí),定義了main()方法的那個(gè)類(lèi)先初始化

以上情況稱為稱對(duì)一個(gè)類(lèi)進(jìn)行“主動(dòng)引用”,除此種情況之外,均不會(huì)觸發(fā)類(lèi)的初始化,稱為“被動(dòng)引用”

接口的加載過(guò)程與類(lèi)的加載過(guò)程稍有不同。接口中不能使用static{}塊。當(dāng)一個(gè)接口在初始化時(shí),并不要求其父接口全部都完成了初始化,只有真正在使用到父接口時(shí)(例如引用接口中定義的常量)才會(huì)初始化。

4 被動(dòng)引用例子

  • 子類(lèi)調(diào)用父類(lèi)的靜態(tài)變量,子類(lèi)不會(huì)被初始化。只有父類(lèi)被初始化。。對(duì)于靜態(tài)字段,只有直接定義這個(gè)字段的類(lèi)才會(huì)被初始化.
  • 通過(guò)數(shù)組定義來(lái)引用類(lèi),不會(huì)觸發(fā)類(lèi)的初始化
  • 訪問(wèn)類(lèi)的常量,不會(huì)初始化類(lèi)
class SuperClass {
    static {
        System.out.println("superclass init");
    }
    public static int value = 123;
}
 
class SubClass extends SuperClass {
    static {
        System.out.println("subclass init");
    }
}
 
public class Test {
    public static void main(String[] args) {
        System.out.println(SubClass.value);// 被動(dòng)應(yīng)用1
        SubClass[] sca = new SubClass[10];// 被動(dòng)引用2
    }
}

程序運(yùn)行輸出

superclass init

123

從上面的輸入結(jié)果證明了被動(dòng)引用1與被動(dòng)引用2

class ConstClass {
    static {
        System.out.println("ConstClass init");
    }
    public static final String HELLOWORLD = "hello world";
}
 
public class Test {
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLOWORLD);// 調(diào)用類(lèi)常量
    }
}

程序輸出結(jié)果

hello world

從上面的輸出結(jié)果證明了被動(dòng)引用3

5 類(lèi)的加載過(guò)程

5.1 加載

“加載”(Loading)階段是“類(lèi)加載”(Class Loading)過(guò)程的第一個(gè)階段,在此階段,虛擬機(jī)需要完成以下三件事情:

  • 1、通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取定義此類(lèi)的二進(jìn)制字節(jié)流。
  • 2、將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
  • 3、在Java堆中生成一個(gè)代表這個(gè)類(lèi)的java.lang.Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問(wèn)入口。

加載階段即可以使用系統(tǒng)提供的類(lèi)加載器在完成,也可以由用戶自定義的類(lèi)加載器來(lái)完成。加載階段與連接階段的部分內(nèi)容(如一部分字節(jié)碼文件格式驗(yàn)證動(dòng)作)是交叉進(jìn)行的,加載階段尚未完成,連接階段可能已經(jīng)開(kāi)始。

5.2驗(yàn)證

驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。

Java語(yǔ)言本身是相對(duì)安全的語(yǔ)言,使用Java編碼是無(wú)法做到如訪問(wèn)數(shù)組邊界以外的數(shù)據(jù)、將一個(gè)對(duì)象轉(zhuǎn)型為它并未實(shí)現(xiàn)的類(lèi)型等,如果這樣做了,編譯器將拒絕編譯。但是,Class文件并不一定是由Java源碼編譯而來(lái),可以使用任何途徑,包括用十六進(jìn)制編輯器(如UltraEdit)直接編寫(xiě)。如果直接編寫(xiě)了有害的“代碼”(字節(jié)流),而虛擬機(jī)在加載該Class時(shí)不進(jìn)行檢查的話,就有可能危害到虛擬機(jī)或程序的安全。

不同的虛擬機(jī),對(duì)類(lèi)驗(yàn)證的實(shí)現(xiàn)可能有所不同,但大致都會(huì)完成下面四個(gè)階段的驗(yàn)證:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。

  • 1、文件格式驗(yàn)證,是要驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理。如驗(yàn)證魔數(shù)是否0xCAFEBABE;主、次版本號(hào)是否正在當(dāng)前虛擬機(jī)處理范圍之內(nèi);常量池的常量中是否有不被支持的常量類(lèi)型……該驗(yàn)證階段的主要目的是保證輸入的字節(jié)流能正確地解析并存儲(chǔ)于方法區(qū)中,經(jīng)過(guò)這個(gè)階段的驗(yàn)證后,字節(jié)流才會(huì)進(jìn)入內(nèi)存的方法區(qū)中存儲(chǔ),所以后面的三個(gè)驗(yàn)證階段都是基于方法區(qū)的存儲(chǔ)結(jié)構(gòu)進(jìn)行的。
  • 2、元數(shù)據(jù)驗(yàn)證,是對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析,以保證其描述的信息符合Java語(yǔ)言規(guī)范的要求??赡馨ǖ尿?yàn)證如:這個(gè)類(lèi)是否有父類(lèi);這個(gè)類(lèi)的父類(lèi)是否繼承了不允許被繼承的類(lèi);如果這個(gè)類(lèi)不是抽象類(lèi),是否實(shí)現(xiàn)了其父類(lèi)或接口中要求實(shí)現(xiàn)的所有方法……
  • 3、字節(jié)碼驗(yàn)證,主要工作是進(jìn)行數(shù)據(jù)流和控制流分析,保證被校驗(yàn)類(lèi)的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的行為。如果一個(gè)類(lèi)方法體的字節(jié)碼沒(méi)有通過(guò)字節(jié)碼驗(yàn)證,那肯定是有問(wèn)題的;但如果一個(gè)方法體通過(guò)了字節(jié)碼驗(yàn)證,也不能說(shuō)明其一定就是安全的。
  • 4、符號(hào)引用驗(yàn)證,發(fā)生在虛擬機(jī)將符號(hào)引用轉(zhuǎn)化為直接引用的時(shí)候,這個(gè)轉(zhuǎn)化動(dòng)作將在“解析階段”中發(fā)生。驗(yàn)證符號(hào)引用中通過(guò)字符串描述的權(quán)限定名是否能找到對(duì)應(yīng)的類(lèi);在指定類(lèi)中是否存在符合方法字段的描述符及簡(jiǎn)單名稱所描述的方法和字段;符號(hào)引用中的類(lèi)、字段和方法的訪問(wèn)性(private、protected、public、default)是否可被當(dāng)前類(lèi)訪問(wèn)

驗(yàn)證階段對(duì)于虛擬機(jī)的類(lèi)加載機(jī)制來(lái)說(shuō),不一定是必要的階段。如果所運(yùn)行的全部代碼確認(rèn)是安全的,可以使用-Xverify:none參數(shù)來(lái)關(guān)閉大部分的類(lèi)驗(yàn)證措施,以縮短虛擬機(jī)類(lèi)加載時(shí)間。

5.3準(zhǔn)備

準(zhǔn)備階段是為類(lèi)的靜態(tài)變量分配內(nèi)存并將其初始化為默認(rèn)值,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。準(zhǔn)備階段不分配類(lèi)中的實(shí)例變量的內(nèi)存,實(shí)例變量將會(huì)在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在Java堆中。

public static int value=123;//在準(zhǔn)備階段value初始值為0 。在初始化階段才會(huì)變?yōu)?23 。

5.4 解析

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

符號(hào)引用(Symbolic Reference):符號(hào)引用以一組符號(hào)來(lái)描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無(wú)歧義地定位到目標(biāo)即可。符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無(wú)關(guān),引用的目標(biāo)并不一定已經(jīng)加載到內(nèi)存中。

直接引用(Direct Reference):直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄。直接引用是與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局相關(guān)的,如果有了直接引用,那么引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

5.5 初始化

類(lèi)初始化是類(lèi)加載過(guò)程的最后一步,前面的類(lèi)加載過(guò)程,除了在加載階段用戶應(yīng)用程序可以通過(guò)自定義類(lèi)加載器參與之外,其余動(dòng)作完全由虛擬機(jī)主導(dǎo)和控制。到了初始化階段,才真正開(kāi)始執(zhí)行類(lèi)中定義的Java程序代碼。

初始化階段是執(zhí)行類(lèi)構(gòu)造器<clinit>()方法的過(guò)程。<clinit>()方法是由編譯器自動(dòng)收集類(lèi)中的所有類(lèi)變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static{}塊)中的語(yǔ)句合并產(chǎn)生的。

6 題目分析

上面很詳細(xì)的介紹了類(lèi)的加載時(shí)機(jī)和類(lèi)的加載過(guò)程,通過(guò)上面的理論來(lái)分析本文開(kāi)門(mén)見(jiàn)上的題目

class SingleTon {
    private static SingleTon singleTon = new SingleTon();
    public static int count1;
    public static int count2 = 0;
 
    private SingleTon() {
        count1++;
        count2++;
    }
 
    public static SingleTon getInstance() {
        return singleTon;
    }
}
 
public class Test {
    public static void main(String[] args) {
        SingleTon singleTon = SingleTon.getInstance();
        System.out.println("count1=" + singleTon.count1);
        System.out.println("count2=" + singleTon.count2);
    }
}

分析:

  • 1:SingleTon singleTon = SingleTon.getInstance();調(diào)用了類(lèi)的SingleTon調(diào)用了類(lèi)的靜態(tài)方法,觸發(fā)類(lèi)的初始化
  • 2:類(lèi)加載的時(shí)候在準(zhǔn)備過(guò)程中為類(lèi)的靜態(tài)變量分配內(nèi)存并初始化默認(rèn)值 singleton=null count1=0,count2=0
  • 3:類(lèi)初始化化,為類(lèi)的靜態(tài)變量賦值和執(zhí)行靜態(tài)代碼快。singleton賦值為new SingleTon()調(diào)用類(lèi)的構(gòu)造方法
  • 4:調(diào)用類(lèi)的構(gòu)造方法后count=1;count2=1
  • 5:繼續(xù)為count1與count2賦值,此時(shí)count1沒(méi)有賦值操作,所有count1為1,但是count2執(zhí)行賦值操作就變?yōu)?附錄:

附錄:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java+Selenium實(shí)現(xiàn)控制瀏覽器的啟動(dòng)選項(xiàng)Options

    Java+Selenium實(shí)現(xiàn)控制瀏覽器的啟動(dòng)選項(xiàng)Options

    這篇文章主要為大家詳細(xì)介紹了如何使用java代碼利用selenium控制瀏覽器的啟動(dòng)選項(xiàng)Options的代碼操作,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2023-01-01
  • IDEA導(dǎo)入Eclipse項(xiàng)目的方法步驟(圖文教程)

    IDEA導(dǎo)入Eclipse項(xiàng)目的方法步驟(圖文教程)

    這篇文章主要介紹了IDEA導(dǎo)入Eclipse項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Java實(shí)現(xiàn)的Windows資源管理器實(shí)例

    Java實(shí)現(xiàn)的Windows資源管理器實(shí)例

    這篇文章主要介紹了Java實(shí)現(xiàn)的Windows資源管理器,實(shí)例分析了基于java實(shí)現(xiàn)windows資源管理器的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • IDEA?服務(wù)器熱部署圖文詳解(On?Update?action/On?frame?deactivation)

    IDEA?服務(wù)器熱部署圖文詳解(On?Update?action/On?frame?deactivation)

    這篇文章主要介紹了IDEA?服務(wù)器熱部署詳解(On?Update?action/On?frame?deactivation),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • 深入理解Java中的Lambda表達(dá)式

    深入理解Java中的Lambda表達(dá)式

    這篇文章主要介紹了深入理解Java中的Lambda表達(dá)式,Lambda在各編程語(yǔ)言中都是非常重要的特性,而Java中則加入得有些太晚...需要的朋友可以參考下
    2015-07-07
  • java中的FileInputStream(輸入流)

    java中的FileInputStream(輸入流)

    這篇文章主要介紹了java中的FileInputStream(輸入流),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java調(diào)用chatgpt接口來(lái)實(shí)現(xiàn)專屬于自己的人工智能助手

    java調(diào)用chatgpt接口來(lái)實(shí)現(xiàn)專屬于自己的人工智能助手

    這篇文章主要介紹了用java來(lái)調(diào)用chatget的接口,實(shí)現(xiàn)自己的聊天機(jī)器人,對(duì)人工智能感興趣的小伙伴可以參考閱讀
    2023-03-03
  • Spring Data JPA中 in 條件參數(shù)的傳遞方式

    Spring Data JPA中 in 條件參數(shù)的傳遞方式

    這篇文章主要介紹了Spring Data JPA中 in 條件參數(shù)的傳遞方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Netty進(jìn)階之EventExecutorGroup源碼詳解

    Netty進(jìn)階之EventExecutorGroup源碼詳解

    這篇文章主要介紹了Netty進(jìn)階之EventExecutorGroup源碼詳解,EventExecutorGroup繼承了JDK的ScheduledExecutroService,那么它就擁有了執(zhí)行定時(shí)任務(wù),執(zhí)行提交的普通任務(wù),需要的朋友可以參考下
    2023-11-11
  • Java中注解的常見(jiàn)用法總結(jié)

    Java中注解的常見(jiàn)用法總結(jié)

    注解(Annotation),也叫元數(shù)據(jù),是JDK1.5及以后版本引入的一個(gè)特性,本文主要為大家介紹了注解的常見(jiàn)用法,需要的小伙伴可以參考一下
    2023-07-07

最新評(píng)論