一個(gè) class 文件被加載到內(nèi)存中的步驟如下圖所示:
裝載
?裝載是指 Java 虛擬機(jī)查找 .class 文件并生成字節(jié)流,然后根據(jù)字節(jié)流創(chuàng)建 java.lang.Class 對(duì)象的過(guò)程。
1. ClassLoader 通過(guò)一個(gè)類的全限定名(包名+類名)來(lái)查找 .class 文件,并生成二進(jìn)制字節(jié)流。其中 class 字節(jié)碼文件的來(lái)源:1).class 文件;2)jar包,zip包;3)網(wǎng)絡(luò)的字節(jié)流。
2. 把 .class 文件的各個(gè)部分分別解析(parse)為 JVM 內(nèi)部特定的數(shù)據(jù)結(jié)構(gòu),并存儲(chǔ)在方法區(qū)。JVM 會(huì)將這些 .lcass 文件的結(jié)果轉(zhuǎn)換為 JVM 內(nèi)部運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
3. 在內(nèi)存中創(chuàng)建一個(gè) java.lang.Class 類型的對(duì)象。程序在運(yùn)行過(guò)程中所有對(duì)該類的訪問(wèn)都通過(guò)這個(gè)類對(duì)象,也就是這個(gè) Class 類型的類對(duì)象是提供給外界訪問(wèn)該類的接口。
加載時(shí)機(jī)
隱式裝載:在程序運(yùn)行過(guò)程中,當(dāng)碰到通過(guò) new 等方式生成對(duì)象時(shí),系統(tǒng)會(huì)隱式調(diào)用 ClassLoader 去裝載對(duì)應(yīng)的 class 到內(nèi)存中。
顯示裝載:在編寫源代碼時(shí),主動(dòng)調(diào)用 Class.forName() 等方法也會(huì)進(jìn)行 class 裝載操作。
鏈接
鏈接過(guò)程分為3步:驗(yàn)證、準(zhǔn)備、解析
1. 驗(yàn)證
目的是為了確保.class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危及虛擬機(jī)本身的安全。
● 文件格式檢驗(yàn):檢驗(yàn)字節(jié)流是否符合class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理
● 元數(shù)據(jù)檢驗(yàn):對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析,以保證其描述的內(nèi)容符合Java語(yǔ)言規(guī)范的要求
● 字節(jié)碼檢驗(yàn):通過(guò)數(shù)據(jù)流和控制流分析,確定程序語(yǔ)義是合法、符合邏輯的
● 符號(hào)引用檢驗(yàn):可以看作是對(duì)類自身以外(常量池中的各種符號(hào)引用)的信息進(jìn)行匹配性校驗(yàn)
2. 準(zhǔn)備
準(zhǔn)備的主要目的是為類中的靜態(tài)變量分配內(nèi)存,并為其設(shè)置“0值”。
public static int value = 100;
在準(zhǔn)備階段,JVM 會(huì)為 value 分配內(nèi)存,并將其設(shè)置為0,而真正的值100 是在初始化階段設(shè)置。
public static final int value = 100;
有 final 關(guān)鍵字修飾的變量會(huì)在準(zhǔn)備階段分配內(nèi)存并設(shè)置值為100。
Java 中基本類型的默認(rèn)“0值”如下:
● 基本類型(int, long, short, char, byte, boolean, float, double)的默認(rèn)值為0;
● 引用類型默認(rèn)值是 null。
3. 解析
解析的任務(wù)是把常量池中的符號(hào)引用轉(zhuǎn)換為直接引用,也就是具體的內(nèi)存地址。在這一階段,JVM 會(huì)將常量池中的類、接口名、字段名、方法名等轉(zhuǎn)換為具體的內(nèi)存地址。
初始化
初始化這一階段是執(zhí)行類構(gòu)造器<cinit>方法的過(guò)程,并真正初始化類變量。例如
public static int value = 100;
在準(zhǔn)備階段,JVM 會(huì)為 value 分配內(nèi)存,并將其設(shè)置為0,而真正的值100 是在初始化階段設(shè)置。
初始化的時(shí)機(jī)
JVM規(guī)范中嚴(yán)格規(guī)定了class初始化的時(shí)機(jī),主要有以下幾種情況會(huì)觸發(fā)class的初始化:
1. 虛擬機(jī)啟動(dòng)時(shí),初始化包含main方法的主類
2. 遇到new指令創(chuàng)建對(duì)象實(shí)例時(shí),如果目標(biāo)對(duì)象類沒(méi)有被初始化則進(jìn)行初始化操作
3. 當(dāng)遇到訪問(wèn)靜態(tài)方法或者靜態(tài)字段的指令時(shí),如果目標(biāo)對(duì)象類沒(méi)有被初始化則進(jìn)行初始化操作
4. 子類的初始化過(guò)程如果發(fā)現(xiàn)其父類還沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其父類的初始化
5. 使用反射API進(jìn)行反射調(diào)用時(shí),如果類沒(méi)有進(jìn)行過(guò)初始化則需要先觸發(fā)其初始化
6. 第一次調(diào)用java.lang.invoke.MethodHandle實(shí)例時(shí)需要初始化MethodHandle指向方法所在的類
初始化類變量
在初始化階段,只會(huì)初始化與類相關(guān)的靜態(tài)賦值語(yǔ)句和靜態(tài)語(yǔ)句,也就是有 static 關(guān)鍵字修飾的信息。沒(méi)有 static 修飾的語(yǔ)句塊在實(shí)例化對(duì)象的時(shí)候才會(huì)執(zhí)行。
Class 初始化和對(duì)象的創(chuàng)建順序
面試題:在代碼中使用 new 創(chuàng)建一個(gè)類的實(shí)例對(duì)象時(shí),類中的靜態(tài)代碼塊、非靜態(tài)代碼塊、構(gòu)造函數(shù)之間的執(zhí)行順序是怎樣的?
對(duì)象的初始化順序:靜態(tài)變量/靜態(tài)代碼塊 -->? 普通代碼塊 --> 構(gòu)造函數(shù)
1. 父類靜態(tài)變量和靜態(tài)代碼塊
2. 子類靜態(tài)變量和靜態(tài)代碼塊
3. 父類普通成員變量和普通代碼塊
4. 父類的構(gòu)造函數(shù)
5. 子類普通成員變量和普通代碼塊
6. 子類的構(gòu)造函數(shù)
總結(jié)
1. 裝載:指查找字節(jié)流,并根據(jù)此字節(jié)流創(chuàng)建類的過(guò)程,裝載過(guò)程成功的標(biāo)志就是在方法區(qū)中成功創(chuàng)建了類所對(duì)應(yīng)的 Class 對(duì)象。
2. 鏈接:指驗(yàn)證創(chuàng)建的類,并將其解析到 JVM 中使之能夠被 JVM 執(zhí)行。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-727596.html
3. 初始化:是將標(biāo)記為 static 的字段進(jìn)行賦值,并且執(zhí)行 static 標(biāo)記的代碼語(yǔ)句。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-727596.html
到了這里,關(guān)于Android---Class 對(duì)象在執(zhí)行引擎中的初始化過(guò)程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!