一、JAVA虛擬機(jī)和JAVA程序的生命周期
JAVA虛擬機(jī)的生命周期和JAVA程序的生命周期一致,當(dāng)我們?cè)诿钚兄星萌雑ava命令運(yùn)行java程序時(shí),java虛擬機(jī)進(jìn)程啟動(dòng),程序運(yùn)行,當(dāng)程序終止時(shí),則JAVA虛擬機(jī)的生命也結(jié)束。
二、類(lèi)的生命周期
1、加載:將.class文件的二進(jìn)制數(shù)據(jù)放到內(nèi)存方法區(qū)中,并在堆區(qū)中創(chuàng)建一個(gè)Class對(duì)象,這個(gè)Class對(duì)象封裝了方法區(qū)的數(shù)據(jù)結(jié)構(gòu),用戶(hù)能通過(guò)Class對(duì)象訪問(wèn)到方法區(qū)中。
2、連接
?。?)驗(yàn)證:驗(yàn)證.class文件是否是通過(guò)JAVA程序編譯出來(lái)的,因?yàn)橛锌赡苓@個(gè).class文件是黑客特意制造出來(lái)的。
?。?)準(zhǔn)備:為類(lèi)中的靜態(tài)變量分配空間,并初始化為默認(rèn)值。
?。?)解析:把類(lèi)的符號(hào)引用變?yōu)橹苯右谩?/p>
3.初始化:為靜態(tài)變量和靜態(tài)塊賦予值。
JAVA程序?qū)τ陬?lèi)的使用方式:
?。?)主動(dòng)使用。
?。?)被動(dòng)使用。
這里注意:
JAVA虛擬機(jī)對(duì)于加載和連接的時(shí)間節(jié)點(diǎn)是很寬松的,沒(méi)有嚴(yán)格規(guī)定,可以提前加載也可以;但是對(duì)于初始化,JAVA虛擬機(jī)規(guī)定當(dāng)某個(gè)類(lèi)被主動(dòng)使用時(shí)才能初始化。
我們把3個(gè)步驟細(xì)講一下:
1、類(lèi)的加載:類(lèi)是通過(guò)類(lèi)加載器進(jìn)行加載。
類(lèi)加載的來(lái)源:
(1)文件系統(tǒng)中的class文件
(2)jar包
?。?)網(wǎng)絡(luò)中下載。
類(lèi)加載目的地:內(nèi)存。
類(lèi)加載器分類(lèi):
?。?)根類(lèi)加載器:沒(méi)有父類(lèi),加載java.lang.*。
?。?)擴(kuò)展類(lèi)加載器:父類(lèi)是根類(lèi)加載器,用于加載jre\lib\ext的jar包。
(3)系統(tǒng)類(lèi)加載器:父類(lèi)是擴(kuò)展類(lèi)加載器,用于加載classpath的jar包。Class scl = Class.getSystemClassLoader();
(4)自定義加載器:自定義加載,通常父類(lèi)是系統(tǒng)類(lèi)加載器。
注意:通過(guò)類(lèi)虛擬機(jī)自帶的(1)(2)(3)加載器是JAVA虛擬機(jī)創(chuàng)建的,而他們加載的類(lèi),他的生命周期是虛擬機(jī)的生命周期,因?yàn)槭冀K被加載器鎖引用。
2、類(lèi)的解析
將符號(hào)引用轉(zhuǎn)換成直接引用。比如:
A函數(shù)調(diào)用了B函數(shù),原本只是符號(hào)引用即標(biāo)明引用了B函數(shù),直接引用是將符號(hào)改成指針指向B函數(shù)。
3、類(lèi)的初始化
規(guī)則:
(1)初始化的靜態(tài)變量都是運(yùn)行時(shí)變量,即不能在編譯時(shí)就能判斷值是多少。
?。?)初始化的順序就是按照代碼的順序執(zhí)行。
?。?)如果初始化子類(lèi)時(shí)父類(lèi)還沒(méi)有被初始化,則先初始化父類(lèi)。
初始化時(shí)機(jī):當(dāng)遇到以下情況會(huì)進(jìn)行初始化。
?。?)new創(chuàng)建實(shí)例、反射創(chuàng)建實(shí)例、clone創(chuàng)建實(shí)例、反序列化創(chuàng)建實(shí)例。
?。?)訪問(wèn)靜態(tài)變量,即讀和寫(xiě)。
(3)調(diào)用靜態(tài)方法。
?。?)啟動(dòng)類(lèi)需要首先初始化。
(5)Class.forName();
注意:
?。?)當(dāng)遇到編譯時(shí)常量,則直接用數(shù)字替換,而不會(huì)導(dǎo)致類(lèi)初始化。比如public static final int a= 3;這就是一個(gè)編譯時(shí)常量。
(2)當(dāng)子類(lèi)對(duì)象調(diào)用父類(lèi)的靜態(tài)變量或方法,則只對(duì)父類(lèi)進(jìn)行初始化。比如:Sub.a,其中a是父類(lèi)的靜態(tài)變量,則只對(duì)Base初始化。
(3)當(dāng)子類(lèi)被初始化時(shí),父類(lèi)一定要先初始化;
但是如果一個(gè)類(lèi)實(shí)現(xiàn)了一個(gè)接口,當(dāng)類(lèi)被初始化時(shí),不用初始化父接口。
只有對(duì)這個(gè)接口進(jìn)行訪問(wèn)時(shí),才會(huì)對(duì)接口進(jìn)行初始化。
?。?)loader.loadClass("....");只是對(duì)類(lèi)的加載,而不是初始化。
類(lèi)加載過(guò)程采用“父親委托機(jī)制”,即如果loader2的父類(lèi)是loader1,loader2想要加載test類(lèi),則先會(huì)檢查loader1是否能夠加載test類(lèi),如果能,則通過(guò)父類(lèi)加載。
運(yùn)行時(shí)包的概念:包名相同,類(lèi)加載器相同。