類裝載器負責把類裝入 Java 虛擬機(JVM)。簡單的應用程序可以用 Java 平臺內置的類裝載工具裝載類;更復雜的應用程序則傾向于定義自己定制的類裝載器。但是,不論使用哪種類裝載器,在類裝載過程中都可能發生許多問題。如果想避免這類問題,需要理解類裝載的基本機制。
類裝載器委托模型
注意:在這個繼承體系中,父類裝載器始終具有優先裝載的權力。比如子類接收到需要裝載Person類的請求,它會先委托給父類去嘗試裝載,父類裝載失敗,子類才會接著嘗試裝載。但是,都被規定了各自的裝載空間。[例如:“System Class Loader” 不會到/jre/lib/* 下去load]
引導類裝載器(也稱作基本(primordial) 類裝載器):
它本身是C++寫的程序。可以獨立運行,它是JVM的運行起點。不能由 Java 代碼實例化。(通常是因為它是作為 VM 本身的一部分實現的。)這個類裝載器可以從啟動的類路徑裝載核心系統類,通常是位于 jre/lib 目錄的 JAR 文件。
擴展(extension) 類裝載器(也稱作標準擴展 類裝載器):
是引導類裝載器的一個孩子。它的主要職責是從擴展目錄裝載類,通常位于 jre/lib/ext 目錄。這提供了簡單地訪問新擴展的能力,例如不同的安全擴展,不需要修改用戶的類路徑即可實現。
系統(system) 類裝載器(也稱作應用程序 類裝載器):
負責從 CLASSPATH 環境變量指定的路徑裝載代碼。默認情況下,這個類裝載器是用戶創建的任何類裝載器的父類。這也是 ClassLoader.getSystemClassLoader() 方法返回的類裝載器。
類裝載的階段
一個類的運行,JVM做會以下幾件事情: 1、類裝載 2、鏈接 3、初始化 4、實例化
一個類的裝載可分成三個階段:類裝載、鏈接和初始化
類裝載的 階段示意圖

裝載 階段包括:找到必要的類(通過查找每個類路徑)并裝載字節碼。在 JVM 中,裝載階段為類對象提供了非常基本的內存結構。在這一階段不處理方法、字段和引用的其他類。所以,類還不能使用。
鏈接 是三個階段中最復雜的一個。可以把它分成三個主要階段:
- 字節碼驗證。 類裝載器對于類的字節碼要做許多檢測,以確保格式正確、行為正確。
- 類準備。 這個階段準備代表每個類中定義的字段、方法和實現接口所必需的數據結構。
- 解析。 這個階段,類裝載器裝載類所引用的其他所有類。可以用許多方式引用類:
在初始化 階段,類中包含的靜態初始化器都被執行。在這一階段末尾,靜態字段被初始化成默認值。
在這三個階段末尾,類被完整地裝載,可以使用了。請注意可以用惰性方式執行類裝載,所以類裝載過程的某些部分可能在第一次使用類的時候[比如:初始化]才執行,而不是在裝載時執行。
舉例說明:
Class.forName(args[0], false,off.getClass().getClassLoader());
告訴JVM不需在load class之后進行initial的工作。這樣,將initial的工作推遲到了newInstance的時候進行。所以,static塊的絕對不是“只在類被第一次實例化的時候才會被僅僅調用一次”,而應
該是在類被初始化的時候,僅僅調用一次
Class.forName(args[0], true,off.getClass().getClassLoader());
告訴JVM在load class之后立即進行initial的工作
顯式裝載與隱式裝載
類裝載的方式有兩種 —— 顯式 或 隱式,兩者之間有些細微差異。
顯式 類裝載發生在使用以下方法調用裝載的類的時候:
- cl.loadClass()(cl 是 java.lang.ClassLoader 的實例)
- Class.forName()(啟動的類裝載器是當前類定義的類裝載器)
當調用其中一個方法的時候,指定的類(以類名為參數)由類裝載器裝載。如果類已經裝載,那么只是返回一個引用;否則,裝載器會通過委托模型裝載類。
隱式 類裝載發生在由于引用、實例化或繼承導致裝載類的時候(不是通過顯式方法調用)。在每種情況下,裝載都是在幕后啟動的,JVM 會解析必要的引用并裝載類。與顯式類裝載一樣,如果類已經裝載了,那么只是返回一個引用;否則,裝載器會通過委托模型裝載類。例如:Person p = new Person();
類的裝載通常組合了顯式和隱式類裝載。例如,類裝載器可能先顯式地裝載一個類,然后再隱式地裝載它引用的所有類。
參考:IBM技術文檔
Goingmm 2006-2-23