1. 程式人生 > >class類載入

class類載入

1. class檔案結構

1. 魔數與Class檔案的版本

Class檔案頭4個位元組為魔數(Magic Number),為0xCAFEBABE。

緊接著4個位元組為Class檔案的版本號,第5、6位元組為次版本號,第7、8位元組為主版本號。

2. 常量池

常量池中主要存放兩大類常量:字面量和符號引用。字面量比較接近於java語言層面的常量概念,如文字字串、宣告為final的常量值等。而符號引用則屬於編譯原理方面的概念,包括了下面三類常量:

  1. 類和介面的全限定名。
  2. 欄位名稱和描述符。
  3. 方法的名稱和描述符。

3. 訪問標誌

用於識別一些類或者介面層次等訪問資訊,包括:這個Class是類還是介面;是否定義為public型別;是否定義為abstract型別;如果是類的話,是否被宣告為final等。

4. 類索引、父類索引與介面索引集合

這三項資料來確定這個類等繼承關係。

5. 欄位表集合

用於描述介面或者類中宣告的變數。

6. 方法表集合

用於描述方法。

7. 屬性表集合

2. Class檔案生命週期

類從被載入到虛擬機器記憶體中開始,到卸載出記憶體為止,它的整個生命週期包括:

1. 載入(Loading)

此階段完成三件事情:

  1. 通過一個類等全限定名來獲取定義此類等二進位制位元組流。
  2. 將這個位元組流所代表的靜態儲存結構轉化為方法區等執行時資料結構。
  3. 在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料等訪問入口。

2. 驗證(Verification)

大致完成下面4個階段等檢驗動作:

  1. 檔案格式驗證。
  2. 元資料驗證。
  3. 位元組碼驗證。
  4. 符號引用驗證。

3. 準備(Preparation)

準備階段是正式為類變數分配記憶體並設定類變數初始值(型別初始值)的階段,這些變數所使用等記憶體都將在方法區中進行分配。

4. 解析(Resolution)

解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程。

5. 初始化(Initialization)

根據程式設計師通過程式制定的主觀計劃去初始化類變數和其他資源,從另外一個角度去表達就是:初始化階段是執行類構造器clinit()方法的過程。

6. 使用(Using)

7. 解除安裝(Unloading)

3. 類與類載入器

對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。這句話表達的更通俗一些:比較兩個類是否“相等”,只有在這兩個類是由同一個類載入器載入等前提下才有意義,否則,即使這兩個類來源於同一個Class檔案,被同一個虛擬機器載入,只要載入他們的類載入器不同,那這兩個類就必定不相等。

這裡所指的“相等”,包括代表類的Class物件的equals()方法、isAssignableFrom()方法、isInstance()方法返回結果,也包括使用instanceof關鍵字做物件所屬關係判定等情況。

4. 雙親委派模型

三種系統提供的類載入器:

1. 啟動類載入器(Bootstrap ClassLoader)

這個類負責載入JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath引數所指定的路徑中的,並且是虛擬機器識別的類庫載入到虛擬機器記憶體中。

2. 擴充套件類載入器(Extension ClassLoader)

這個載入器由sun.misc.Launcher$ExtClassLoader實現,它負責載入JAVA_HOME\lib\ext目錄中的,或者被java.ext.dirsz系統變數所指定的路徑中的所有類庫,開發者可以直接使用擴充套件類載入器。

3. 應用程式類載入器(Application ClassLoader)

這個載入器由sun.misc.Launcher$AppClassLoader實現。由於這個類載入器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱它為系統類載入器。它負責載入使用者類經上所指定的類庫,開發者可以直接使用這個類載入器,如果應用程式中沒有自定義過自己的類載入器,一般情況下這個就是程式中預設的類載入器。
image

4. 雙親委派模型過程

如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委託給父類載入器去完成,每一個層次的類載入器都是如此,因此所有的載入請求最終都應該傳送到頂層的啟動類載入器中,只有當父載入器反饋自己無法完成這個載入請求時,子載入器才會嘗試自己去載入。

JDK1.2之後已不提倡使用者再去覆蓋loadClass()方法,而應當把自己的類載入邏輯寫到findClass()方法中,在loadClass()方法的邏輯裡如果父類載入失敗,則會呼叫自己的findClass()方法來完成載入,這樣就可以保障新寫出來的類載入器是符合雙親委派規則的。