從一道面試題來認(rèn)識java類加載時機(jī)與過程

1 開門見山

以前曾經(jīng)看到過一個java的面試題,當(dāng)時覺得此題很簡單昌犹,可是自己把代碼運(yùn)行起來,可是結(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);
    }
}

錯誤答案

count1=1

count2=1

正確答案

count1=1

count2=0

為神馬奉瘤?為神馬?這要從java的類加載時機(jī)說起。

2 類的加載時機(jī)

類從被加載到虛擬機(jī)內(nèi)存中開始狠半,直到卸載出內(nèi)存為止,它的整個生命周期包括了:加載颤难、驗(yàn)證神年、準(zhǔn)備、解析行嗤、初始化已日、使用和卸載這7個階段。其中昂验,驗(yàn)證捂敌、準(zhǔn)備和解析這三個部分統(tǒng)稱為連接(linking)艾扮。

[圖片上傳失敗...(image-23f8bc-1559204646892)]

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

3 何時開始類的初始化

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

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

以上情況稱為稱對一個類進(jìn)行“主動引用”枫吧,除此種情況之外,均不會觸發(fā)類的初始化宇色,稱為“被動引用”

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

4 被動引用例子

  1. 子類調(diào)用父類的靜態(tài)變量抢蚀,子類不會被初始化镀层。只有父類被初始化。。對于靜態(tài)字段唱逢,只有直接定義這個字段的類才會被初始化.
  2. 通過數(shù)組定義來引用類吴侦,不會觸發(fā)類的初始化
  3. 訪問類的常量,不會初始化類
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);// 被動應(yīng)用1
        SubClass[] sca = new SubClass[10];// 被動引用2
    }
}

程序運(yùn)行輸出 superclass init
123

從上面的輸入結(jié)果證明了被動引用1與被動引用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)用類常量
    }
}

程序輸出結(jié)果
hello world
從上面的輸出結(jié)果證明了被動引用3

5 類的加載過程

5.1 加載

“加載”(Loading)階段是“類加載”(Class Loading)過程的第一個階段坞古,在此階段备韧,虛擬機(jī)需要完成以下三件事情:

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

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

5.2 驗(yàn)證

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

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

不同的虛擬機(jī)暴氏,對類驗(yàn)證的實(shí)現(xiàn)可能有所不同延塑,但大致都會完成下面四個階段的驗(yàn)證:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證答渔、字節(jié)碼驗(yàn)證和符號引用驗(yàn)證关带。

1、文件格式驗(yàn)證沼撕,是要驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范宋雏,并且能被當(dāng)前版本的虛擬機(jī)處理芜飘。如驗(yàn)證魔數(shù)是否0xCAFEBABE;主磨总、次版本號是否正在當(dāng)前虛擬機(jī)處理范圍之內(nèi)燃箭;常量池的常量中是否有不被支持的常量類型……該驗(yàn)證階段的主要目的是保證輸入的字節(jié)流能正確地解析并存儲于方法區(qū)中,經(jīng)過這個階段的驗(yàn)證后舍败,字節(jié)流才會進(jìn)入內(nèi)存的方法區(qū)中存儲招狸,所以后面的三個驗(yàn)證階段都是基于方法區(qū)的存儲結(jié)構(gòu)進(jìn)行的。

2邻薯、元數(shù)據(jù)驗(yàn)證裙戏,是對字節(jié)碼描述的信息進(jìn)行語義分析,以保證其描述的信息符合Java語言規(guī)范的要求厕诡±郯瘢可能包括的驗(yàn)證如:這個類是否有父類;這個類的父類是否繼承了不允許被繼承的類灵嫌;如果這個類不是抽象類壹罚,是否實(shí)現(xiàn)了其父類或接口中要求實(shí)現(xiàn)的所有方法……

3、字節(jié)碼驗(yàn)證寿羞,主要工作是進(jìn)行數(shù)據(jù)流和控制流分析猖凛,保證被校驗(yàn)類的方法在運(yùn)行時不會做出危害虛擬機(jī)安全的行為。如果一個類方法體的字節(jié)碼沒有通過字節(jié)碼驗(yàn)證绪穆,那肯定是有問題的辨泳;但如果一個方法體通過了字節(jié)碼驗(yàn)證,也不能說明其一定就是安全的玖院。

4菠红、符號引用驗(yàn)證,發(fā)生在虛擬機(jī)將符號引用轉(zhuǎn)化為直接引用的時候难菌,這個轉(zhuǎn)化動作將在“解析階段”中發(fā)生试溯。驗(yàn)證符號引用中通過字符串描述的權(quán)限定名是否能找到對應(yīng)的類;在指定類中是否存在符合方法字段的描述符及簡單名稱所描述的方法和字段郊酒;符號引用中的類遇绞、字段和方法的訪問性(private、protected猎塞、public试读、default)是否可被當(dāng)前類訪問

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

5.3 準(zhǔn)備

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

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

5.4 解析

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

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

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

5.5 初始化

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

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

6 題目分析

上面很詳細(xì)的介紹了類的加載時機(jī)和類的加載過程,通過上面的理論來分析本文開門見上的題目

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

原文:https://www.cnblogs.com/javaee6/p/3714716.html?utm_source=tuicool&utm_medium=referral

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咱士,一起剝皮案震驚了整個濱河市立由,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌序厉,老刑警劉巖锐膜,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弛房,居然都是意外死亡道盏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荷逞,“玉大人媒咳,你說我怎么就攤上這事≈衷叮” “怎么了涩澡?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坠敷。 經(jīng)常有香客問我妙同,道長,這世上最難降的妖魔是什么膝迎? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任粥帚,我火速辦了婚禮,結(jié)果婚禮上弄抬,老公的妹妹穿的比我還像新娘茎辐。我一直安慰自己,他們只是感情好掂恕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布拖陆。 她就那樣靜靜地躺著,像睡著了一般懊亡。 火紅的嫁衣襯著肌膚如雪依啰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天店枣,我揣著相機(jī)與錄音速警,去河邊找鬼。 笑死鸯两,一個胖子當(dāng)著我的面吹牛闷旧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钧唐,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼忙灼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了钝侠?” 一聲冷哼從身側(cè)響起该园,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帅韧,沒想到半個月后里初,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忽舟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年双妨,在試婚紗的時候發(fā)現(xiàn)自己被綠了淮阐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡斥难,死狀恐怖枝嘶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哑诊,我是刑警寧澤群扶,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站镀裤,受9級特大地震影響竞阐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暑劝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一骆莹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧担猛,春花似錦幕垦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒸走,卻和暖如春仇奶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背比驻。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工该溯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人别惦。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓狈茉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掸掸。 傳聞我的和親對象是個殘疾皇子论皆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355