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