? Java中類的加載過程(如Dog類):
?
- 通過類型信息定位Dog.class文件。
- 載入Dog.class文件,創建相應的Class對象。
- 執行父類的靜態字段定義時初始化語句和父類的靜態初始化塊。
- 執行子類的靜態字段定義時初始化語句和子類的靜態初始化塊。
- 當使用new Dog()方式時,在堆上為Dog對象分配存儲空間,并清零分配的存儲空間。
- 執行父類的字段定義時初始化語句和父類的構造函數。
- 執行子類的字段定義時初始化語句和子類的構造函數。
(參考Java編程思想,部分是自己測試的結果,還沒有看到相關資料,這個順序和C#的順序好像有比較大的差別)
?
我們知道每個Java類編譯后會生成一個.class文件,里面除了存儲了類的字節碼之外,還存儲了和該類對應的Class對象的信息(包含一些用于反射的信息)。在JVM中,通過類加載器(ClassLoader)來實現Class對象的創建。
?
在Object類中有public的getClass()方法,我們可以通過該方法獲取與之對應的Class對象。在獲取類對應的Class對象后,我們就可以通過newInstance()方法或者通過獲取其Constructor的方法來創建該類的實例,也可以通過Class對象獲取類或實例相關的更多信息,如方法,字段等,即反射的功能。另外我們也可以通過Class.forName()方法,通過傳入類字符串的方式來創建類的實例。(通過Class對象創建類實例和通過forName方法創建類實例是否有聯系呢?這兩種創建類的實例和直接通過new創建類實例是否也有聯系呢?)
?
然而Dog.class文件是如何定位的?Dog.class文件是如何被加載并解析的?Class對象又是如何生成的呢?這就是ClassLoader需要解決的問題,也是這篇文章中接下來要講述的。
?
ClassLoader的工作原理
?
在Java內部實現了三種類型的ClassLoader:Bootstrap ClassLoader,Extension ClassLoader,?System ClassLoader。這三個ClassLoader通過parent形成一條單向鏈,其中Bootstrap處于鏈尾,而System ClassLoader處于鏈頭。
?
Bootstrap ClassLoader
Bootstrap ClassLoader用于在啟動JVM時加載類,以使JVM能正常工作,因而它是用Native代碼實現的,最早被創建出來,處于最底層。
Bootstrap ClassLoader將搜索Java核心庫(%JAVA_HOME%\jre\lib),如rt.jar、i18n.jar等,在這些核心庫中包含了Java的核心類,即Bootstrap ClassLoader用于加載Java的核心類,包括java.lang、java.io等包中的類。
?
Extension ClassLoader
Extension ClassLoader位于鏈的中間層,它將搜索特定的標準擴展目錄。該標準擴展目錄在不同的JVM實現中不一定相同,在sun的JVM中,它是%JRE_HOME% \lib\ext,具體路徑可以通過java.ext.dirs系統屬性值獲取。該標準擴展目錄存在的目的在于擴展和共享,應用程序廠商可以將部分共享庫放置于此,而不是各自程序的目錄下的多份拷貝。在開發過程中,我們也可以把部分常用的庫放置于此,而不必每次都去配置環境。
?
System ClassLoader
System ClassLoader位于鏈的最頂層,它將搜索CLASSPATH中配置的目錄和jar文件。
?
三種ClassLoader的協同工作
既然系統中有三種不同的ClassLoader,那么一個類的加載是用那個ClassLoader呢?在Java中,是通過“代理模型(delegation model)”來決定使用哪個ClassLoader來加載類的(讀取.class文件,并創建相應的Class對象)。當一個類需要被加載的時候,系統默認通過System ClassLoader來加載該類;然而System ClassLoader并不立即加載該類,它會將加載的行為代理給其parent去加載,只有當其parent不能加載該類的時,System ClassLoader才去搜索CLASSPATH中的目錄和jar文件以加載該類,若找到對應的.class文件,則加載該類,否則拋出ClassNotFoundException。當它將加載的行為代理給Extension ClassLoader并最終代理給Bootstrap ClassLoader的時候,他們也做的是相似的事情。從設計的角度,這其實就是一種職責鏈模式(Chain of Responsibility Pattern)。
?
那么Java為什么需要這種代理模型呢?答案是出于安全的考慮。Java是一種安全的編程語言,它會對每個加載的類執行安全檢查,但是對Java核心庫中的類是不執行安全檢查的(即他們是受JVM信賴的)。(什么是安全檢查?以我現在所知,就是用戶可以通過security manager來控制特定目錄的訪問權限,如何使用這些控制將會是另一個主題。對安全檢查是否還有其他的呢?)那么假如用戶可以加載自己編寫的和核心庫同名的類(如java.lang.Object),那么這些用戶編寫的類就可以繞過安全檢查,從而為一些惡意用戶提供了一種破壞的途徑。然而在使用這種代理模型后,類的加載首先會代理到Bootstrap ClassLoader中實行加載,如果它發現當前核心類庫中可以加載對應的類,系統會加載核心庫中的類,而不會加載用戶編寫的類。
?
但是如果只是這樣,還不足以保證類的加載的安全。因為用戶完全可以定義自己的ClassLoader,在自己的ClassLoader中破壞這種代理模型,那么用戶自己寫的java.lang.Object就可以被加載了。為了解決這個問題,在Java中認為兩個類名相同的實例,如果加載他們的ClassLoader不同,那么他們是不同的類型,即他們之間不能轉換,而且用instanceof操作符返回的是false。這樣的話,即使用戶加載了自己的java.lang.Object類,它也不是系統認為可以信賴的java.lang.Object,那么它就繞不過安全檢查機制。(以上的描述只是我的猜想,我還沒有看到相關的資料提到該問題,因而有待驗證。)
?
有人說使用這種機制可以實現兼容性的升級,在軟件的舊版本中使用某個舊版本的類,在升級的某些新部分中可以使用相同類的新版本,即實現內存中保留一個類的兩個版本。我認為這種不應該是一種推薦的做法,在內存中保留一個類的兩個版本極易在運行是引起一些莫名其妙的錯誤。
?
很可惜,我沒能找到以上三種ClassLoader的源碼,看不了內部的實現。在Java中,它們都是通過ClassLoader這個抽象類來表示的。以下簡單的介紹一下ClassLoader內部的成員
(其中Extension ClassLoader的實現類為:sun .misc.Launcher$ExtClassLoader)
?
來自?http://dlevin.iteye.com/blog/772604