前言
Java的類的生命周期為:加載、驗證、準備、解析、初始化、使用、卸載七個生命周期。其中,加載、驗證、準備、解析、初始化可以稱之為類的加載過程,而加載和類加載過程是有區(qū)別的,它只是類加載過程的一個階段。

類加載的過程
加載
這個階段主要完成三件事情:
1)通過一個類的全限定類名來獲取描述此類的二進制字節(jié)流。
2)將這個字節(jié)流代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)運行時的數(shù)據(jù)結(jié)構(gòu)。
3)在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口。
類加載完成后,虛擬機外部的二進制字節(jié)流就按照虛擬機所需的格式存儲在方法區(qū)中,然后再內(nèi)存中實例化一個java.lang.Class對象(對于HotSpot虛擬機而言,class對象不存在于Java堆,而在方法區(qū)中創(chuàng)建),這個對象將作為外部程序訪問方法區(qū)中這些類型數(shù)據(jù)的外部接口。
驗證
這一階段的目的主要是確保class文件的字節(jié)流中所包含的信息符合虛擬機規(guī)范的要求,并且不會危害虛擬機,確保虛擬機自身安全。
主要進行四部分的驗證:
1)文件格式的驗證,class文件是否符合虛擬機class文件格式規(guī)范,比如是否以0xCAFEBABE開頭、主次版本號是否和當前虛擬機匹配,等等。
2)元數(shù)據(jù)驗證,主要對字節(jié)碼描述的信息進行語義分析,確保語言描述符合java語言規(guī)范,如是否繼承了被final修飾的類等等。
3)字節(jié)碼驗證,主要是通過數(shù)據(jù)流和控制流分析,確保程序語義是合法的、符合邏輯的,這一個階段也是最復雜的驗證階段。
4)符號引用驗證,對類自身以外的信息(常量池中的各種符號引用)進行匹配性校驗,通常校驗的內(nèi)容有:
4.1符號引用中通過字符串描述的全限定類名能否找到對應的類。
4.2在指定類中是否存在符合方法的字段描述符以及簡單名稱描述的方法和字段。
4.3符號引用中類、字段、方法的訪問性是否可被當前類訪問。
準備
準備階段是正式為類變量分配內(nèi)存并設置類變量初始值的階段,這些變量所需要的內(nèi)存,都將在方法區(qū)中進行分配。這里有概念容易混淆:第一,這時候進行內(nèi)存分配僅包括類變量(被static修飾的),不包括實例變量,實例變量將隨著對象實例化時一起在Java堆中分配內(nèi)存。第二,這里所說的初始值,通常情況下是數(shù)據(jù)類型的0值或者null值。
比如:public static int value = 123; value在準備階段后的初始值是0而不是123。
如果類變量定義的是一個常量的話,那么在準備階段會將value直接賦值。比如:
public static final int value = 123;,value在準備階段會被賦值為123。
解析
解析階段主要是完成符號引用替換為直接引用。主要包括類或接口的解析、字段解析、類方法解析、接口方法解析。
初始化
初始化階段主要是給類的成員變量進行初始化值,在這個階段,虛擬機會自動創(chuàng)建一個<cinit>方法,我們稱之為類構(gòu)造器,這個方法的要執(zhí)行的內(nèi)容主要包括兩部分,類成員變量的初始化和靜態(tài)代碼塊。它們的執(zhí)行順序按照在類中的定義的順序。其中,靜態(tài)代碼塊中可以對其前面定義的類成員變量賦值和訪問,但不可以對其后定義的類成員變量訪問,但可以賦值。
static {
i = 2; // 給變量賦值可以正常編譯通過
//System.out.println(i); // 編譯不通過,會提示"非法向前引用"
}
static int i = 1;