一個面試題引出來的class加載時機

這里看到的這個題
看起來很簡單,就自己寫了一下,然后看原文鏈接還是沒看懂,這里自己寫一下自己的想法励背。
我們看一下這個題:

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 + " count2=" + singleTon.count2);
    }
}

乍一看俄精,按照我們的思路應(yīng)該是輸出

count1=1 count2=1

我們輸出一下就會發(fā)現(xiàn)其實是輸出的是:

count1=1 count2=0

其實我們的思路是對的疙教,我們知道為變量賦值是在初始化階段,這個時候其實我們的這些SingleTon類包括count1侯养,count2的內(nèi)存其實都已經(jīng)在準(zhǔn)備階段創(chuàng)建出來了畔况,只是說是默認(rèn)值,真正的賦值是在初始化階段慧库。
只不過我們忽略了一個靜態(tài)變量初始化的順序問題跷跪,JVM加載的時候靜態(tài)變量是按照順序加載的,這個類里面一共有三個靜態(tài)變量齐板,所以這三步是順序執(zhí)行的:

    1吵瞻、 private static SingleTon singleTon = new SingleTon();
    2葛菇、 public static int count1;
    3、 public static int count2 = 0;

第一步橡羞,加載SingleTon眯停,這里調(diào)用了new SingleTon()賦值,也就是會調(diào)用構(gòu)造方法里面的值卿泽。

private SingleTon() {
        count1++;
        count2++;
    }

這里的值如果打印的話會發(fā)現(xiàn)count1莺债,count2由0變成了1。
這個時候1签夭、SingleTon構(gòu)造結(jié)束齐邦。會繼續(xù)往后執(zhí)行2跟3。給count1第租,count2賦值措拇。
第二步,我們看到count1是沒有做賦值操作的慎宾,那么就不需要進(jìn)行賦值丐吓,保持原來的就可以了,這個時候count1的值是1趟据。
第三步券犁,給count2賦值為0
所以,最終結(jié)果是count1=1 count2=0
我們可以驗證一下給這三步換個順序就很明顯了~


這個其實是考驗我們對虛擬機類加載過程的了解之宿,先畫個圖族操。


類加載過程

一、加載
虛擬機規(guī)范要求比被,虛擬在這個階段需要完成以下3件事情:

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

加載階段完成后,虛擬機外部的二進(jìn)制字節(jié)流就按照虛擬機所需的格式存儲在方法區(qū)之中等缀。也就是ClassLoader起作用的階段枷莉。

二、連接
驗證尺迂、準(zhǔn)備笤妙、解析稱為連接階段。
1噪裕、驗證
驗證是連接階段的第一步蹲盘,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機的要求,并且不會危害虛擬機自身的安全膳音。

2召衔、準(zhǔn)備
準(zhǔn)備階段是正是為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存將都在方法區(qū)中進(jìn)行分配祭陷。這里的進(jìn)行內(nèi)存分配的類變量是被static修飾的變量苍凛,而不包括實例變量趣席,實例變量將會在對象實例化時分配在堆中。而final static修飾的會直接賦值為對應(yīng)的值醇蝴。

數(shù)據(jù)類型 零值 數(shù)據(jù)類型 零值
int 0 boolean false
long 0L float 0.0f
short (short)0 double 0.0d
char '\u0000' reference null
byte (byte)0

3宣肚、解析
解析階段是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程,比如在方法A中使用方法B悠栓,A () {B () ;}霉涨,這里的B () 就是符號引用,初學(xué)java時我們都是知道這是java的引用闸迷,以為B指向B方法的內(nèi)存地址嵌纲,但是這是不完整的,這里的B只是一個符號引用腥沽,它對于方法的調(diào)用沒有太多的實際意義逮走,可以這么認(rèn)為,他就是給程序員看的一個標(biāo)志今阳,讓程序員知道师溅,這個方法可以這么調(diào)用,但是B方法實際調(diào)用時是通過一個指針指向B方法的內(nèi)存地址盾舌,這個指針才是真正負(fù)責(zé)方法調(diào)用墓臭,他就是直接引用。

三妖谴、初始化
初始化階段是類加載過程的最后一步窿锉,前面的類加載過程中,除了在加載階段用戶應(yīng)用程序可以通過自定義類加載器參與之外膝舅,其余動作完全由虛擬機主導(dǎo)和控制嗡载,到了初始化階段,才真正開始執(zhí)行勒種定義的Java程序代碼仍稀。

初始化結(jié)束后一個類的加載就結(jié)束了洼滚。這個時候我們的Class類和類變量(static變量)已經(jīng)在方法區(qū)了。

這個時候我們再回過頭來看上面的那個題其實就很清晰了技潘,其實就是一個初始化階段賦值順序的問題遥巴。

class SingleTon {
    private static SingleTon singleTon = new SingleTon(); // 2?? 給singleTon分配空間并設(shè)置初始值為null  5??singleTon初始化為 new SingleTon()
    public static int count1; // 3?? count1分配空間并設(shè)置初始值為0 7?? count1沒有初始化賦值,故保持當(dāng)前的值享幽,當(dāng)前值為1
    public static int count2 = 0; // 4?? count2分配空間并設(shè)置初始值為0 8?? count2 初始化賦值為0

    private SingleTon() { //  6?? SingleTon構(gòu)造方法
        //當(dāng)前 count1 = 0  count2 = 0
        count1++;
        count2++;
        //++完之后 count1 = 1  count2 = 1
    }

    public static SingleTon getInstance() {
        return singleTon;
    }
}
 
public class Test {
    public static void main(String[] args) {
        SingleTon singleTon = SingleTon.getInstance();  // 1?? 加載SingleTon類铲掐; 9?? 調(diào)用getInstance()方法
        System.out.println("count1=" + singleTon.count1 + "count2=" + singleTon.count2); 
    }
}

我們來分析下這里主要涉及的類加載的階段:
一、加載
① 加載SingleTon類
二值桩、連接
1迹炼、驗證
2、準(zhǔn)備
② 給singleTon分配空間并設(shè)置初始值為null
③ count1分配空間并設(shè)置初始值為0
④ count2分配空間并設(shè)置初始值為0
3颠毙、解析
三斯入、初始化
⑤singleTon初始化為 new SingleTon()
⑥ SingleTon構(gòu)造方法 (結(jié)束后 count1 = 1 count2 = 1)
⑦ count1沒有初始化值,故保持當(dāng)前的值蛀蜜,當(dāng)前值為1
⑧ count2 初始化賦值為0
⑨ 調(diào)用getInstance()方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刻两,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子滴某,更是在濱河造成了極大的恐慌磅摹,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霎奢,死亡現(xiàn)場離奇詭異户誓,居然都是意外死亡,警方通過查閱死者的電腦和手機幕侠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門帝美,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晤硕,你說我怎么就攤上這事悼潭。” “怎么了舞箍?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵舰褪,是天一觀的道長。 經(jīng)常有香客問我疏橄,道長占拍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任捎迫,我火速辦了婚禮晃酒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘立砸。我一直安慰自己掖疮,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布颗祝。 她就那樣靜靜地躺著浊闪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪螺戳。 梳的紋絲不亂的頭發(fā)上搁宾,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音倔幼,去河邊找鬼盖腿。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的翩腐。 我是一名探鬼主播鸟款,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茂卦!你這毒婦竟也來了何什?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤等龙,失蹤者是張志新(化名)和其女友劉穎处渣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛛砰,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡罐栈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泥畅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荠诬。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涯捻,靈堂內(nèi)的尸體忽然破棺而出浅妆,到底是詐尸還是另有隱情,我是刑警寧澤障癌,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布凌外,位于F島的核電站,受9級特大地震影響涛浙,放射性物質(zhì)發(fā)生泄漏康辑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一轿亮、第九天 我趴在偏房一處隱蔽的房頂上張望疮薇。 院中可真熱鬧,春花似錦我注、人聲如沸按咒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽励七。三九已至,卻和暖如春奔缠,著一層夾襖步出監(jiān)牢的瞬間掠抬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工校哎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留两波,地道東北人瞳步。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像腰奋,于是被迫代替她去往敵國和親单起。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348