類加載器

類加載器基本概念

顧名思義,類加載器(class loader)用來加載 Java 類到 Java 虛擬機中。一般來說,Java 虛擬機使用 Java 類的方式如下:Java 源程序(.java 文件)在經過 Java 編譯器編譯之後就被轉換成 Java 字節代碼(.class 文件)。類加載器負責讀取 Java 字節代碼,並轉換成 java.lang.Class 類的一個實例。每個這樣的實例用來表示一個 Java 類。通過此實例的 newInstance()方法就可以創建出該類的一個對象。

類加載器的代理模式

類加載器在嘗試自己去查找某個類的字節代碼並定義它時,會先代理給其父類加載器,由父類加載器先去嘗試加載這個類,依次類推。在介紹代理模式背後的動機之前,首先需要說明一下 Java 虛擬機是如何判定兩個 Java 類是相同的。 Java 虛擬機不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣。 只有兩者都相同的情況,才認爲兩個類是相同的。即便是同樣的字節代碼,被不同的類加載器加載之後所得到的類,也是不同的。

瞭解了這一點之後,就可以理解代理模式的設計動機了。代理模式是爲了保證 Java 核心庫的類型安全。所有 Java 應用都至少需要引用 java.lang.Object類,也就是說在運行的時候,java.lang.Object這個類需要被加載到 Java 虛擬機中。如果這個加載過程由 Java 應用自己的類加載器來完成的話,很可能就存在多個版本的 java.lang.Object類,而且這些類之間是不兼容的。通過代理模式,對於 Java 核心庫的類的加載工作由引導類加載器來統一完成,保證了 Java 應用所使用的都是同一個版本的 Java 核心庫的類,是互相兼容的。

加載類的過程

在前面介紹類加載器的代理模式的時候,提到過類加載器會首先代理給其它類加載器來嘗試加載某個類。這就意味着真正完成類的加載工作的類加載器和啓動這個加載過程的類加載器,有可能不是同一個。真正完成類的加載工作是通過調用 defineClass來實現的;而啓動類的加載過程是通過調用 loadClass來實現的。前者稱爲一個類的定義加載器(defining loader),後者稱爲初始加載器(initiating loader)。在 Java 虛擬機判斷兩個類是否相同的時候,使用的是類的定義加載器。也就是說,哪個類加載器啓動類的加載過程並不重要,重要的是最終定義這個類的加載器。兩種類加載器的關聯之處在於:一個類的定義加載器是它引用的其它類的初始加載器。
方法 loadClass()拋出的是 java.lang.ClassNotFoundException異常;方法 defineClass()拋出的是 java.lang.NoClassDefFoundError異常。
類加載器在成功加載某個類之後,會把得到的 java.lang.Class類的實例緩存起來。下次再請求加載該類的時候,類加載器會直接使用緩存的類的實例,而不會嘗試再次加載。也就是說,對於一個類加載器實例來說,相同全名的類只加載一次,即 loadClass方法不會被重複調用。

類加載器加載類得大致過程:

(1)Java 源程序(.java 文件)在經過 Java 編譯器編譯之後就被轉換成 Java 字節代碼(.class 文件)。
(2)類加載器負責讀取 Java 字節代碼,並轉換成 java.lang.Class 類的一個實例。
(2.1)啓動類的加載過程是通過調用 loadClass來實現的(initiating loader)—拋出 java.lang.ClassNotFoundException異常
(2.2)真正完成類的加載工作是通過調用 defineClass來實現的(defining loader)—拋出 java.lang.NoClassDefFoundError異常(判斷兩個類是否相同的時候,使用的是類的定義加載器)

參考:https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

發佈了223 篇原創文章 · 獲贊 23 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章