類的加載逼泣、連接趴泌、初始化

轉(zhuǎn)載、引用請標明出處
http://www.reibang.com/p/853701433b3a
本文出自zhh_happig的簡書博客拉庶,謝謝

以下內(nèi)容嗜憔,是本人學習筆記和工作中的總結(jié),僅供大家參考氏仗,有誤的地方還請指正

java虛擬機

執(zhí)行一個java程序吉捶,都會啟動一個java虛擬機的進程,進程里面包含一個主線程來執(zhí)行程序皆尔,當程序執(zhí)行完了之后呐舔,java虛擬機進程就消亡了。
在如下幾種情況下慷蠕,java虛擬機將結(jié)束生命周期

  • 執(zhí)行了System.exit()方法
  • 程序正常執(zhí)行結(jié)束
  • 程序在執(zhí)行過程中遇到了異成浩矗或錯誤而異常終止
  • 由于操作系統(tǒng)出現(xiàn)錯誤而導(dǎo)致java虛擬機進程終止

一 類的加載、連接流炕、初始化

1 加載:查找并加載類的二進制數(shù)據(jù)

  • 類記載器ClassLoader將java的.class文件中的二進制數(shù)據(jù)讀入到內(nèi)存中澎现,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)中
  • 加載后,虛擬機在堆區(qū)創(chuàng)建一個與該類對應(yīng)java.lang.Class對象浪感,不管類的對象有多少個昔头,與此類對應(yīng)的Class對象只有一個,Class對象用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)影兽,所以類里面的內(nèi)容都可以通過Class對象獲取揭斧。Class對象是反射的入口。
  • 類的加載并不是你首次使用的時候去加載峻堰,而是預(yù)料到某個類要被使用的時候預(yù)先加載它讹开。
  • 程序只有被加載到內(nèi)存中,才能被執(zhí)行

2 連接

  • 驗證:確保被加載的類的正確性
    • 通過javac生成的.class文件肯定是正確的捐名,但是有的直接手動生成.calss可能是不符合java字節(jié)碼規(guī)則的旦万,這里要驗證。
  • 準備:為類的靜態(tài)變量分配內(nèi)存镶蹋,并將其初始化為默認值
    • 到這一步成艘,類的加載、連接贺归、初始化還沒有完成淆两,不會生成對象,所以所有的實例對象都不會分配內(nèi)存拂酣,只有靜態(tài)變量會被分配內(nèi)存秋冰,初始化為默認值:int型初始化為0,boolean類型初始化為false婶熬,引用類型初始化為null剑勾。順序:從上至下埃撵。
  • 解析:把類中的符號引用轉(zhuǎn)換為直接引用
    • 在Worker類中調(diào)用car.run(),Car類的run()方法虽另。car.run()---這是符號引用暂刘,在解析階段java虛擬機會把這個符號引用替換為一個指針,該指針指向了Car類的run()方法在方法區(qū)內(nèi)的內(nèi)存位置洲赵,這個指針就是直接引用鸳惯。

3 初始化:為類的靜態(tài)變量賦予正確的初始值

  • 正確的初始值是在程序中被賦的值商蕴,比如 int a = 3叠萍,這一步將3賦值給a;所以a經(jīng)過了兩次賦值:第一次是連接準備階段绪商,a先會被賦默認初始值0苛谷,第二就是初始化為程序中賦的值。
  • 靜態(tài)變量的初始化有兩種方式
    • 在靜態(tài)變量的聲明處進項初始化
    • 在靜態(tài)代碼塊中進行初始化
  • 初始化步驟
    • 假如這個類還沒有被加載和連接格郁,先進行加載和連接
    • 假如這個類存在父類腹殿,父類還沒有初始化,先初始化直接的父類
    • 初始化語句執(zhí)行順序:從上至下依次執(zhí)行
  • 初始化時機
    • 類的主動使用
    • 當java虛擬機初始化類時例书,它所有的父類都已經(jīng)被初始化了锣尉,但是這條規(guī)則不適用與接口
    • 在初始化一個類時,并不會先初始化它實現(xiàn)的接口
      • 初始化一個接口時决采,并不會初始化它的父接口
      • 只有當程序首次使用特定接口的靜態(tài)變量時自沧,才會導(dǎo)致該接口的初始化

二 java程序?qū)︻惖氖褂梅绞?/h1>

主動使用
被動使用

主動使用(6種),除了以下6種树瞭,其他類的使用全是被動使用

  • 創(chuàng)建類的實例
    • new出來一個類的實例對象拇厢,即為對這個類的主動使用
  • 訪問某個類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值
    • int b = Test.a; 這個也是對Test類的主動使用
    • final修飾的靜態(tài)變量要注意:如果這個變量在編譯時就能確定它的值晒喷,就不會導(dǎo)致類被初始化孝偎,例如 public static final int a = 6/3
    • 如果這個變量要在運行時才能確定它的值,才會導(dǎo)致類被初始化凉敲,例如public static final int a = new Random().nextInt(100)
  • 調(diào)用類的靜態(tài)方法
    • int b = Test.add()
    • 只有當訪問的靜態(tài)變量或靜態(tài)方法確實在當前類或當前接口中定義時衣盾,才可以認為是對類和接口的主動使用,詳見練習題5中例子爷抓。
  • 反射創(chuàng)建類的實例
  • 初始化一個類的子類
    • 初始化一個父類的子類势决,也是對這個父類的主動使用
  • Java虛擬機啟動時被標注為啟動的類
    • 啟動程序的類,包含main方法的類

所有的java虛擬機實現(xiàn)必須在每個類或接口被java程序 首次主動使用 時才初始化他們,換句話說就是以上6種情況废赞,而且是第1次主動使用才會在初始化徽龟。其他情況,如被動使用唉地,或第二次主動使用据悔,都不會執(zhí)行類的初始化传透。

調(diào)用ClassLoader類的loadClass方法加載一個類,并不是對類的主動使用极颓,不會導(dǎo)致類的初始化朱盐。調(diào)用ClassLoader類的loadClass方法只是執(zhí)行了加載操作。

二 示例演示——增加理解

示例1

class Singleton{

    private static Singleton singleton = new Singleton();
    public static int counter1;
    public static int counter2 = 0;

    private Singleton(){
        counter1 ++;
        counter2 ++;
    }

    public static Singleton getInstance(){
        return singleton;
    }

}

public class JVMTest {

    public static void main(String[] args){
        Singleton singleton = Singleton.getInstance();
        System.out.println("counter1 = " + singleton.counter1);
        System.out.println("counter2 = " + singleton.counter2);
    }

}

輸出結(jié)果
1
0

在類的連接--準備階段菠隆,singleton被賦默認值null兵琳,counter1和counter2被賦默認值0;初始化的時候骇径,先初始化singleton躯肌,創(chuàng)建實例,在Singleton 構(gòu)造方法中counter1++破衔,counter2++后清女,counter1和counter2變成了1,然后在再初始化counter1和counter2晰筛,counter1沒有被賦值嫡丙,所以還是1, counter2被賦值了0读第,所以counter2最終為0曙博。

如果將上面的部分代碼順序改一下:

public static int counter1;
public static int counter2 = 0;
private static Singleton singleton = new Singleton();
輸出結(jié)果
1
1
如果看懂了上面的代碼,這個不難理解怜瞒。

示例2

public class Test2 {

    public static void main(String[] args){
        System.out.println(FinalTest.x);
    }

}

class FinalTest{

    public final static int x = 1/3;
    static{
        System.out.println("FinalTest static block");
    }

}

輸出結(jié)果
0

final修飾的靜態(tài)變量要注意:如果這個變量在編譯時就能確定它的值父泳,就不會導(dǎo)致類被初始化,例如
public static final int x = 1/3

public class Test2 {

    public static void main(String[] args){
        System.out.println(FinalTest.x);
    }


}

class FinalTest{

    public final static int x = new Random().nextInt(100);
    static{
        System.out.println("FinalTest static block");
    }

}

輸出結(jié)果
FinalTest static block
75

如果這個變量要在運行時才能確定它的值盼砍,才會導(dǎo)致類被初始化尘吗,例如
public static final int x = new Random().nextInt(100)

示例3

public class Test3 {

    static {
        System.out.println("Test3 static block");
    }

    public static void main(String[] args){
        System.out.println(Child.b);
    }

}

class Parent{

    static int a = 3;
    static{
        System.out.println("Parent static block");
    }

}


class Child extends Parent{

    static int b = 4;
    static{
        System.out.println("Child static block");
    }

}

輸出結(jié)果?
Test3 static block
Parent static block
Child static block
4

Test3是程序入口類浇坐,最先被加載初始化睬捶;先加載初始化父類,再子類近刘。

示例4

public class Test4 {

    static {
        System.out.println("Test4 static block");
    }

    public static void main(String[] args){
        Parent2 parent2;
        System.out.println("-------------");

        parent2 = new Parent2();
        System.out.println(Parent2.a);
        System.out.println(Child2.b);
    }

}

class Parent2{

    static int a = 3;
    static{
        System.out.println("Parent2 static block");
    }

}


class Child2 extends Parent2{

    static int b = 4;
    static{
        System.out.println("Child2 static block");
    }

}

輸出結(jié)果
Test4 static block
-------------
Parent2 static block
3
Child2 static block
4

Child2擒贸、Parent2是由同一個類加載器加載的,所以Parent2初始化了, Child2初始化的時候就不會再去初始化父類了觉渴。
PS: 如果有兩個加載器:A類的加載器介劫,B類的加載器,AB不是父子關(guān)系案淋,即使A類的加載器已經(jīng)初始化了Child2類座韵,在B類的加載中還是可以再去初始化Child2類的。詳解請看后續(xù)的 Java類加載器 文章

為什么下面這一行代碼不去初始化Parent2呢?

...
Parent2 parent2;
...

因為這只是聲明了一個變量誉碴,并沒有主動使用類宦棺,所以不會初始化。

示例5

public class Test5 {

    public static void main(String[] args){
        System.out.println(Child3.a);
        Child3.doSomething();
    }

}

class Parent3{

    static int a = 3;
    static{
        System.out.println("Parent3 static block");
    }

    static void doSomething(){
        System.out.println("doSomething");
    }

}


class Child3 extends Parent3{

    static{
        System.out.println("Child3 static block");
    }

}

輸出結(jié)果黔帕?
Parent3 static block
3
doSomething

Child3.a代咸,Child3.doSomething() 為什么Child3沒有別初始化?
只有當訪問的靜態(tài)變量或靜態(tài)方法確實在當前類或當前接口中定義時成黄,才可以認為是對類和接口的主動使用呐芥。而Child3.a,Child3.doSomething()調(diào)用的靜態(tài)變量或靜態(tài)方法不是在Child2類中定義的奋岁,而是在父類中定義的思瘟,所以不會對Child2進行初始化。

示例6

class C{

    static {
        System.out.println("class C");
    }

}

public class Test1 {

    public static void main(String[] args) throws Exception{
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        Class<?> clazz = loader.loadClass("com.zhh.jvm.loadClass.C");//加載 C 類
        System.out.println("------------");
        clazz = Class.forName("com.zhh.jvm.loadClass.C");
    }

}

輸出結(jié)果厦取?
------------
class C

loader.loadClass("com.zhh.jvm.loadClass.C");
調(diào)用ClassLoader類的loadClass方法加載一個類潮太,并不是對類的主動使用管搪,不會導(dǎo)致類的初始化虾攻。調(diào)用ClassLoader類的loadClass方法只是執(zhí)行了加載操作。clazz = Class.forName("com.zhh.jvm.loadClass.C");是反射創(chuàng)建類的實例更鲁,是類的主動使用霎箍,所以導(dǎo)致類被初始化。

以上內(nèi)容澡为,是本人學習筆記和工作中的總結(jié)漂坏,僅供大家參考,有誤的地方還請指正

轉(zhuǎn)載媒至、引用請標明出處
http://www.reibang.com/p/853701433b3a
本文出自zhh_happig的簡書博客顶别,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拒啰,隨后出現(xiàn)的幾起案子驯绎,更是在濱河造成了極大的恐慌,老刑警劉巖谋旦,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剩失,死亡現(xiàn)場離奇詭異,居然都是意外死亡册着,警方通過查閱死者的電腦和手機拴孤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甲捏,“玉大人演熟,你說我怎么就攤上這事∷径伲” “怎么了芒粹?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵蚕冬,是天一觀的道長。 經(jīng)常有香客問我是辕,道長囤热,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任获三,我火速辦了婚禮旁蔼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疙教。我一直安慰自己棺聊,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布贞谓。 她就那樣靜靜地躺著限佩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裸弦。 梳的紋絲不亂的頭發(fā)上祟同,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音理疙,去河邊找鬼晕城。 笑死,一個胖子當著我的面吹牛窖贤,可吹牛的內(nèi)容都是我干的砖顷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼赃梧,長吁一口氣:“原來是場噩夢啊……” “哼滤蝠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起授嘀,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤物咳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粤攒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體所森,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年夯接,在試婚紗的時候發(fā)現(xiàn)自己被綠了焕济。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盔几,死狀恐怖晴弃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤上鞠,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布际邻,位于F島的核電站,受9級特大地震影響芍阎,放射性物質(zhì)發(fā)生泄漏世曾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一谴咸、第九天 我趴在偏房一處隱蔽的房頂上張望轮听。 院中可真熱鬧,春花似錦岭佳、人聲如沸血巍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽述寡。三九已至,卻和暖如春叶洞,著一層夾襖步出監(jiān)牢的瞬間鲫凶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工京办, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掀序,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓惭婿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叶雹。 傳聞我的和親對象是個殘疾皇子财饥,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353