Java&Android 基礎(chǔ)知識梳理(5) - 類加載&對象實例化

一扔傅、概述

虛擬機的類加載機制定義:把描述類的數(shù)據(jù)從Class文件(一串二進制的字節(jié)流)加載到內(nèi)存,并對數(shù)據(jù)進行校驗诡蜓、轉(zhuǎn)換解析和初始化熬甫,最終形成被虛擬機直接使用的Java類型。

Java語言里万牺,類型的加載罗珍、連接和初始化過程都是在程序運行期間完成的,Java里天生可以動態(tài)擴展的語言特性就是依賴運行期動態(tài)加載和動態(tài)連接這個特點實現(xiàn)的脚粟。

用戶可以通過Java預(yù)定義的和自定義類加載器覆旱,讓一個本地的應(yīng)用程序可以在運行時從網(wǎng)絡(luò)或其他地方加載一個二進制流作為程序代碼的一部分。

二核无、類加載的時機

2.1 類加載包含那些階段

類從被加載到虛擬機內(nèi)存中開始扣唱,到卸載出內(nèi)存,所經(jīng)過的生命周期有:

  • 1.加載
  • 2.驗證
  • 3.準備
  • 4.解析
  • 5.初始化
  • 6.使用
  • 7.卸載

其中2-4統(tǒng)稱為連接团南,上面的過程有幾個需要注意的點:

  • 加載噪沙、驗證、準備吐根、初始化正歼、卸載這五個階段按順序按部就班地開始,在一個階段執(zhí)行的過程中有可能調(diào)用拷橘、激活另外一個階段局义。
  • 解析階段有可能在初始化之后開始,這是為了支持Java語言的運行時綁定冗疮。

2.2 類加載觸發(fā)的時機

有且僅有下面五種情況必須立即對類進行初始化:

  • 第一種:遇到new/getstatic/putstatic/invokestatic4條字節(jié)碼指令時萄唇,如果類沒有進行過初始化,則需要先觸發(fā)其初始化术幔,場景:
    • 使用new關(guān)鍵字實例化對象
    • 讀取或設(shè)置一個類的靜態(tài)字段(被final修飾另萤,已在編譯期把結(jié)果放入常量池的字段除外)
    • 調(diào)用一個類的靜態(tài)方法
        //1.new關(guān)鍵字.
        LoadInvokeClass loadInvokeClass = new LoadInvokeClass();
        //2.訪問靜態(tài)變量
        int content = LoadInvokeClass.sContent;
        //3.調(diào)用靜態(tài)方法.
        LoadInvokeClass.staticMethod();
  • 第二種:使用java.lang.reflect包的方法對類進行反射調(diào)用的時候,如果類沒有進行過初始化诅挑,則需要先觸發(fā)其初始化四敞。
        try {
            Class<?> mClass = Class.forName("com.example.lizejun.repojavalearn.load.LoadInvokeClass");
        } catch (Exception e) { e.printStackTrace(); }
  • 第三種:當初始化一個類的時候,如果需要初始化其父類拔妥,但是發(fā)現(xiàn)父類沒有初始化忿危、那么需要先觸發(fā)其父類的初始化。
        //其中LoadInvokeClass是LoadInvokeClassChild的父類.
        LoadInvokeClassChild classChild = new LoadInvokeClassChild();
  • 第四種:當虛擬機啟動時毒嫡,用戶需要指定一個要執(zhí)行的主類(包含main()方法),虛擬機會先初始化這個主類。
  • 第五種:使用JDK 1.7的動態(tài)語言支持時兜畸,如果一個java.lang.invoke.MethodHandle實例最后的解析結(jié)果REF_getStatic/REF_putStatic/REF_invokeStatic的句柄方法努释,并且這個方法句柄所對應(yīng)的類沒有進行過初始化,則需要先觸發(fā)其初始化咬摇。

2.3 被動引用

2.2中談到的都是主動引用伐蒂,除此之外,所有引用類的方法都稱為被動引用肛鹏,而被動引用不會觸發(fā)類的初始化

  • 類初始化時逸邦,如果父類沒有被初始化,那么會先初始化父類在扰,這一過程將一直遞歸到Object為止缕减,但是不會去初始化它所實現(xiàn)的接口,即當我們初始化ClassChild的時候芒珠,只會先初始化ClassParent桥狡,但不會初始化ClassInterface
public interface ClassInterface {}

public class ClassParent implements ClassInterface {
    static {
        System.out.println("load ClassParent");
    }
}

public class ClassChild extends ClassParent {
    static {
        System.out.println("load ClassChild");
    }
}
  • 接口初始化時皱卓,不要求父接口全部初始化裹芝,只有真正用到了父接口的時候(如引用接口中定義的常量),那么才會初始化娜汁。
  • 當訪問某個類的靜態(tài)域時嫂易,不會觸發(fā)父類的初始化或者子類的初始化,即使靜態(tài)域被子類或子接口或者它的實現(xiàn)類所引用掐禁,我們給ClassChild添加一個靜態(tài)屬性怜械,訪問這個靜態(tài)屬性不會初始化ClassParent
public class ClassChild extends ClassParent {

    public static int sNumber;

    static {
        System.out.println("load ClassChild");
    }
}
  • 如果一個靜態(tài)變量是編譯時常量穆桂,則對它的引用不會引起定義它的類的初始化宫盔,如下面訪問sNumber,那么不會引起ClassChild的實例化享完。
public class ClassChild extends ClassParent {

    public static final int sNumber = 2;

    static {
        System.out.println("load ClassChild");
    }
}
  • 通過數(shù)組定義來引用類灼芭,不會觸發(fā)此類的初始化。
ClassChild[] children = new ClassChild[10];

三般又、類加載的過程

3.1 加載

在"加載"階段彼绷,虛擬機需要完成以下三件事情:

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

3.2 驗證

"驗證"階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當前虛擬機的要求堕义,并且不會危害自身的安全猜旬,大致會完成下面四個階段的校驗動作:

  • 文件格式驗證
  • 元數(shù)據(jù)驗證
  • 字節(jié)碼驗證
  • 符號引用驗證

3.3 準備

"準備"階段是正式為類變量(被static修飾,而不是實例變量)分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進行分配洒擦。

  • 對于static并且非final的類變量椿争,將被初始化為數(shù)據(jù)類型的零值。
  • 對于staticfinal的類變量熟嫩,在這個階段就會被初始化為ConstantValue屬性所指定的值秦踪。

3.4 解析

“解析”階段是虛擬機將常量池的符號引用替換為直接引用的過程,包括:

  • 類或接口的解析
  • 字段解析
  • 類方法解析
  • 接口方法解析

3.5 初始化

根據(jù)程序員通過程序指定的主觀計劃去初始化類變量和其它資源掸茅,也就是執(zhí)行類構(gòu)造器<clinit>()方法的過程:

  • <clinit>方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊中的語句合并而成椅邓,順序是由語句在源文件中出現(xiàn)的順序決定的。靜態(tài)語句塊只能訪問到定義在它之前的變量昧狮,對于定義在它后面的變量只能賦值不能訪問景馁。

  • <clinit>()方法與類的構(gòu)造函數(shù)不同,它不需要顯示地調(diào)用父類構(gòu)造器陵且,虛擬機會保證在子類的<clinit>()方法執(zhí)行前裁僧,父類的<clinit>()方法已經(jīng)執(zhí)行完畢,因此在虛擬機中第一個杯知行的<clinit>()方法的類肯定是java.lang.Object慕购。

  • 父類的靜態(tài)語句塊要優(yōu)先于子類的變量賦值操作聊疲。

  • 如果一個類中沒有靜態(tài)語句塊,也沒有對類變量的賦值操作沪悲,那么編譯器可以不為這個類生成<clinit>()方法获洲。

  • 接口不能接口中僅有變量初始化的賦值操作,但執(zhí)行接口的<clinit>()方法不需要先執(zhí)行父接口的<clinit>()方法殿如,只有當父接口中定義的變量使用時贡珊,父接口才會初始化,另外涉馁,接口的實現(xiàn)類在初始化時也一樣不會執(zhí)行接口的<clinit>()方法门岔。

  • 虛擬機會保證一個類的<clinit>()方法在多線程環(huán)境中被正確地加鎖、同步烤送。

四寒随、類加載器

4.1 概念

類加載器用來“通過一個類的全限定名來獲取描述此類的二進制字節(jié)流”。

4.2 類與類加載器

類加載器用于實現(xiàn)類的加載動作帮坚,除此之外妻往,任意一個類,都需要由它加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性试和。

每一個類加載器讯泣,都擁有一個獨立的類名稱空間,比較兩個類是否相等阅悍,只有在兩個類由同一個類加載器加載的前提下才有意義好渠。

相等代表類的Class對象的equals方法昨稼,isAssignableFrom方法,isInstance方法拳锚。

4.3 雙親委派模型

絕大部分Java程序都會用到以下三種系統(tǒng)提供的類加載器:

  • 啟動類加載器
  • 擴展類加載器
  • 應(yīng)用類加載器

類加載器之間的層次關(guān)系悦昵,稱為類加載器的雙親委派模型,這個模型要求除了頂層的啟動類加載器外晌畅,其余的類都應(yīng)當有自己的父類加載器,一般使用組合來復(fù)用父加載器的代碼寡痰。

雙親委派模型的工作過程:如果一個類加載器收到了類加載的請求抗楔,它首先不會去嘗試加載這個類,而是把這個請求委派給父類加載器去完成拦坠,只有當父類加載器反饋自己無法完成這個加載請求時连躏,子加載器才會嘗試自己加載。

五贞滨、對象實例化

在類加載過程完畢后入热,如果需要進行實例化對象就需要經(jīng)過一下步驟,按優(yōu)先加載父類晓铆,再到子類的順序執(zhí)行:

  • 加載父類構(gòu)造器
  • 為父類實例對象分配存儲空間并賦值
  • 執(zhí)行父類的初始化塊
  • 執(zhí)行父類構(gòu)造函數(shù)
  • 加載子類加載器
  • 為子類實例對象分配存儲控件并賦值
  • 執(zhí)行子類的初始化塊
  • 執(zhí)行子類構(gòu)造函數(shù)

我們用一個簡單的例子:
其中ClassOther是一個單獨的類:

public class ClassOther {

    public int mNumber;

    public ClassOther() {
        System.out.println("ClassOther Constructor");
    }

    public void setNumber(int number) {
        this.mNumber = number;
    }

    public int getNumber() {
        return mNumber;
    }
}

ClassChild則繼承于ClassChild

public class ClassParent {

    {
        System.out.println("ClassParent before mClassParentContent");
    }

    private ClassOther mClassParentContent = new ClassOther(10);

    {
        System.out.println("ClassParent after mClassParentContent=" + mClassParentContent.mNumber);
    }

    public ClassParent(int number) {
        mClassParentContent.setNumber(number);
        System.out.println("ClassParent Constructor, mClassParentContent=" + mClassParentContent.mNumber);
    }


}

public class ClassChild extends ClassParent {

    {
        System.out.println("ClassChild before a");
    }

    private int mClassChildContent = 1;

    {
        System.out.println("ClassChild after mClassChildContent=" + mClassChildContent);
    }

    public ClassChild() {
        super(2);
        System.out.println("ClassChild Constructor");
    }
}

當我們實例化一個ClassChild對象時勺良,調(diào)用的順序如下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骄噪,隨后出現(xiàn)的幾起案子尚困,更是在濱河造成了極大的恐慌,老刑警劉巖链蕊,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件事甜,死亡現(xiàn)場離奇詭異,居然都是意外死亡滔韵,警方通過查閱死者的電腦和手機逻谦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陪蜻,“玉大人邦马,你說我怎么就攤上這事〈衙螅” “怎么了勇婴?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嘱腥。 經(jīng)常有香客問我耕渴,道長,這世上最難降的妖魔是什么齿兔? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任橱脸,我火速辦了婚禮础米,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘添诉。我一直安慰自己屁桑,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布栏赴。 她就那樣靜靜地躺著蘑斧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪须眷。 梳的紋絲不亂的頭發(fā)上竖瘾,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音花颗,去河邊找鬼捕传。 笑死,一個胖子當著我的面吹牛扩劝,可吹牛的內(nèi)容都是我干的庸论。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼棒呛,長吁一口氣:“原來是場噩夢啊……” “哼聂示!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起簇秒,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤催什,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宰睡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蒲凶,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年拆内,在試婚紗的時候發(fā)現(xiàn)自己被綠了旋圆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡麸恍,死狀恐怖灵巧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抹沪,我是刑警寧澤刻肄,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站融欧,受9級特大地震影響敏弃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜噪馏,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一麦到、第九天 我趴在偏房一處隱蔽的房頂上張望绿饵。 院中可真熱鬧,春花似錦瓶颠、人聲如沸拟赊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吸祟。三九已至,卻和暖如春桃移,著一層夾襖步出監(jiān)牢的瞬間欢搜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工谴轮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吹埠。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓第步,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缘琅。 傳聞我的和親對象是個殘疾皇子粘都,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容