Java字節(jié)代碼:byte[]
Java類在JVM的表現(xiàn)形式:Class類的對(duì)象;
Java源代碼被編譯成class字節(jié)碼 :
Java字節(jié)代碼 --> Class類的對(duì)象:
加載:把Java字節(jié)碼byte[]轉(zhuǎn)換成JVM中的java.lang.Class類的對(duì)象;
鏈接:Java類的鏈接指的是將Java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程。
初始化:主要是執(zhí)行靜態(tài)代碼塊和初始化靜態(tài)域;
Java類的加載
作用 ?{想學(xué)習(xí)Java的請(qǐng)留言 有資料分享}
把Java字節(jié)碼轉(zhuǎn)換成JVM中的java.lang.Class類的對(duì)象;
通過一個(gè)類的全限定名獲取描述此類的二進(jìn)制字節(jié)流;
將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)保存為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);
在java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為訪問方法區(qū)的入口;
類加載器分類
啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載?JAVA_HOME\lib?目錄中的,或通過-Xbootclasspath參數(shù)指定路徑中的,且被虛擬機(jī)認(rèn)可(按文件名識(shí)別,如rt.jar)的類;
擴(kuò)展類加載器(Extension ClassLoader):負(fù)責(zé)加載?JAVA_HOME\lib\ext?目錄中的,或通過java.ext.dirs系統(tǒng)變量指定路徑中的類庫;
應(yīng)用程序類加載器(Application ClassLoader):負(fù)責(zé)加載用戶路徑(classpath)上的類庫;
雙親委派模型工作過程:
當(dāng)一個(gè)類加載器收到類加載任務(wù),優(yōu)先交給其父類加載器去完成,因此最終加載任務(wù)都會(huì)傳遞到頂層的啟動(dòng)類加載器,只有當(dāng)父類加載器無法完成加載任務(wù)時(shí),才會(huì)嘗試執(zhí)行加載任務(wù)。
雙親委派模型有什么好處?
比如位于rt.jar包中的類java.lang.Object,無論哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的啟動(dòng)類加載器進(jìn)行加載,確保了Object類在各種加載器環(huán)境中都是同一個(gè)類。
重要特征
層次組織結(jié)構(gòu):每個(gè)類加載器都有一個(gè)父類加載器,形成tree結(jié)構(gòu);
代理模式:一個(gè)類加載器既可以自己完成Java類的定義工作,也可以代理給其它的類加載器來完成;
Java類的鏈接
Java類的鏈接指的是將Java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程。
包含的步驟:
驗(yàn)證:確保Java類的二進(jìn)制表示在結(jié)構(gòu)上是完全正確的,主要包括格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證;
準(zhǔn)備:創(chuàng)建Java類中的靜態(tài)域,并將這些域的值設(shè)為默認(rèn)值;
在準(zhǔn)備階段,為類變量(static修飾)在方法區(qū)中分配內(nèi)存并設(shè)置初始值。
private static int var = 100;
準(zhǔn)備階段完成后,var值為0,而不是100。在初始化階段,才會(huì)把100賦值給val,但是有個(gè)特殊情況:
private static final int VAL= 100;
在編譯階段會(huì)為VAL生成ConstantValue屬性,在準(zhǔn)備階段虛擬機(jī)會(huì)根據(jù)ConstantValue屬性將VAL賦值為100。
解析:解析階段是將常量池中的符號(hào)引用替換為直接引用的過程,解析過程可能導(dǎo)致其他的Java類被加載;
符號(hào)引用使用一組符號(hào)來描述所引用的目標(biāo),可以是任何形式的字面常量,定義在Class文件格式中。
直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或則能間接定位到目標(biāo)的句柄。
Java類的初始化
初始化階段是執(zhí)行類構(gòu)造器clinit方法的過程,clinit方法由類變量的賦值動(dòng)作和靜態(tài)語句塊按照在源文件出現(xiàn)的順序合并而成,該合并操作由編譯器完成。
方法clinit對(duì)于類或接口不是必須的,如果一個(gè)類中沒有靜態(tài)代碼塊,也沒有靜態(tài)變量的賦值操作,那么編譯器不會(huì)生成
方法clinit方法與實(shí)例構(gòu)造器不同,不需要顯式的調(diào)用父類的
為了防止多次執(zhí)行clinit,虛擬機(jī)會(huì)確保clinit方法在多線程環(huán)境下被正確的加鎖同步執(zhí)行,如果有多個(gè)線程同時(shí)初始化一個(gè)類,那么只有一個(gè)線程能夠執(zhí)行clinit方法,其它線程進(jìn)行阻塞等待,直到clinit執(zhí)行完成。
注意:執(zhí)行接口的clinit方法不需要先執(zhí)行父接口的clinit,只有使用父接口中定義的變量時(shí),才會(huì)執(zhí)行。
初始化過程的主要操作是執(zhí)行靜態(tài)代碼塊和初始化靜態(tài)域。
在一個(gè)類被初始化之前,它的直接父類也需要被初始化。
類初始化場景
虛擬機(jī)中嚴(yán)格規(guī)定了有且只有5種情況必須對(duì)類進(jìn)行初始化。
執(zhí)行new、getstatic、putstatic和invokestatic指令;
使用reflect對(duì)類進(jìn)行反射調(diào)用;
初始化一個(gè)類的時(shí)候,父類還沒有初始化,會(huì)事先初始化父類;
啟動(dòng)虛擬機(jī)時(shí),需要初始化包含main方法的類;
在JDK1.7中,如果java.lang.invoke.MethodHandler實(shí)例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個(gè)方法句柄對(duì)應(yīng)的類沒有進(jìn)行初始化;
不會(huì)觸發(fā)類初始化的情況
通過子類引用父類的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化,而不會(huì)觸發(fā)子類的初始化。
輸出結(jié)果為:parent init! 不會(huì)初始化Child類。
定義對(duì)象數(shù)組,不會(huì)觸發(fā)該類的初始化。
常量在編譯期間會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用定義常量的類,不會(huì)觸發(fā)定義常量所在的類。
在編譯階段,Const類中常量A的值100存儲(chǔ)到Init類的常量池中,這兩個(gè)類在編譯成class文件之后就沒有聯(lián)系了。
通過類名獲取Class對(duì)象,不會(huì)觸發(fā)類的初始化。
通過Class.forName加載指定類時(shí),如果指定參數(shù)initialize為false時(shí),也不會(huì)觸發(fā)類初始化,其實(shí)這個(gè)參數(shù)是告訴虛擬機(jī),是否要對(duì)類進(jìn)行初始化。
通過ClassLoader默認(rèn)的loadClass方法,也不會(huì)觸發(fā)初始化動(dòng)作;