Java和其他語言不同的是,Java是運行於Java虛擬機器(JVM)。這就意味著編譯後的代碼是以一種和平台無關的格式儲存的,而不是某種特定的機器上啟動並執行格式。這種格式和傳統的可執行代碼格式有很多重要的區別。具體來說,不同於C或者C++程式,Java程式不是一個獨立的可執行檔,而是由很多分開的類檔案組成,每個類檔案對應一個Java類。 另外,這些類檔案並不是馬上載入到記憶體,而是當程式需要的時候才載入。 類載入器就是Java虛擬機器中用來把類載入到記憶體的工具。而且,Java類載入器也是用Java實現的。這樣你就不需要對Java虛擬機器有深入的理解就可以很容易建立自己的類載入器了。
java類載入器 :
java中預設有三種類載入器:引導類載入器,擴充類載入器,系統類別載入器(也叫應用類載入器)
類載入器是Java最強大的特徵之一。但是開發人員常常忘記類載入組件。類載入器是在運行時負責尋找和載入類檔案的類。Java允許使用不同的類載入器,甚至自訂的類載入器。
Java 程式包含很多類檔案,每一個都與單個Java類相對應,這些類檔案不像靜態C程式,一次性載入入記憶體,它們隨時需要隨時載入。這就是類載入器與眾不同的地 方。它從源檔案(通常是.class 或 .jar檔案)獲得不依賴平台的位元組碼,然後將它們載入到JVM記憶體空間,所以它們能被解釋和執行。預設狀態下,應用程式的每個類由 java.lang.ClassLoader載入。因為它可以被繼承,所以可以自由地加強其功能。
使用自訂類載入器的原因
預設的 java.lang.ClassLoader僅僅可以從載入本地檔案系統的類。Java被設計成不論本地磁碟或網路都有足夠的彈性載入類,並且可以在載入 之前處理特殊事物。例如:應用程式可以檢查Web網站或FTP上插入類的更新版本並且自動校正數位簽章確保執行可信任的代碼。許多眾所周知的軟體都使用自 己的類載入器。
通常預設載入器是所謂的bootstrap類載入器;它負責載入諸如java.lang.Object等關鍵類和加 載其他rt.jar檔案的運行時代碼到記憶體。因為Java語言規範沒有提供bootstrap類載入器的詳細資料,不同的JVM可能有不同的類載入器。如 果看到網頁上有applets在運行,則它使用的是自訂類載入器。嵌入到瀏覽器中的applet閱讀器包含了可以訪問遠程伺服器上網站的類載入器,它可 以通過HTTP載入原始位元組碼檔案,並且在JVM中將它們轉換成類。
類載入器(除了bootstrap類載入器)有父類載入器,這些父類是基本載入器的載入器執行個體。最重要的一點是設定正確的父載入器。然後可以使用 類載入器的getParent()方法實現委派類請求(例如:自訂類載入器找不到使用專門方法的類時)。此時必須為將父載入器作為 java.lang.ClassLoader構造器的參數:
public class MyClassLoader extends ClassLoader{ public MyClassLoader() { super(MyClassLoader.class.getClassLoader()); } }
loadClass(String name)方法是ClassLoader的入口。名字參數是完全資格類名(FQCN),例如關於包類名。如果父載入器設定正確,當請求 MyClassLoader中的loadClass(String name)方法載入類,但又找不到需要載入的類時,則首先會詢問父載入器。如果父載入器也找不到此類,則調用findClass(String name)方法。預設狀態下findClass(String name)會拋出ClassNotFoundException例外,很多開發人員都很清楚這個例外。自訂類載入器的開發人員都希望從 java.lang.ClassLoader繼承時跳過這個方法。
findClass()方法的目標是為MyClassLoader容納所有專門代碼,此時不需要重複其他代碼(例如當載入失敗時調用系統 ClassLoader)。在此方法中,ClassLoader需要從原檔案中擷取位元組碼。一旦找到位元組碼則會調用defineClass()方法。 ClassLoader執行個體調用此方法是非常重要的。因此,如果兩個ClassLoader執行個體定義了來自不同或相同原檔案的位元組碼,則被定義的類也將區 別對待。
我們給出兩個相似的類載入器MyClassLoader1 和 MyClassLoader2,它們都可以從相同的源檔案找到MyCoolClass位元組碼。如果一個程式通過這兩個載入器分別獨立載入 MyCoolClass執行個體(coolClass1通過MyClassLoader1載入, coolClass2通過MyClassLoader2載入),MyCoolClass.class能夠被獨立定義。執行下面的代碼:
MyCoolClass coolClass1 = (MyCoolClass)coolClass2;
將得到一個ClassCastException例外。(開發人員如果沒有很好的理解類載入機制則經常碰到這樣的情況。)因為它們是不同的載入器 所定義的,JVM將它們看成不同的類。雖然它們是相同類型的類並且從相同的源檔案載入,但是變數coolClass1和coolClass2不相容。
不論是否跳過findClass() 或 loadClass(),getSystemClassLoader()方法將以實際ClassLoader對象的形式直接存取系統 ClassLoader。也可以通過調用findSystemClass(String name)方法間接訪問。getParent()方法允許獲得父載入器。Listing A給出了可以啟動並執行自訂類載入器樣本。
為什麼要建立類載入器?
既然Java虛擬金已經有了類載入器,我們還要自己建立其他的呢?問得好。預設的類載入器只知道如何從本地系統載入類。當你的程式完全在原生編譯的話,預設的類載入器一般都工作的很好。但是Java中最激動人心的地方之一就是很容易的從網路上而不只是本地載入類。
舉個例子,瀏覽器可以通過自訂的類載入器載入類。 還有很多載入類的方式。除了簡單的從本地或者網路外,你還可以通過自訂Java中最激動人心的地方之一:
* 執行非信任代碼前自動驗證數位簽章
* 根據使用者提供的密碼解密代碼
* 根據使用者的需要動態建立類
你關心的任何東西都能方便的以位元組碼的形式整合到你的應用中
自訂類載入器的例子
如果你已經使用過JDK(Java軟體開發包)中的appletviewer(小應用程式瀏覽器)或者其他Java嵌入式瀏覽器,你就已經使用了自訂類載入器了。Sun剛剛發布Java語言的時候,最令人興奮的一件事就是觀看Java如何執行從遠程網站下載的代碼。執行從遠端站台通過HTTP串連傳送來的位元組碼看起來有點不可思議。之所以能夠工作,因為Java有安裝自訂類載入器的能力。小應用程式瀏覽器包含了一個類載入器,這個類載入器不從本地找Java類,而是訪問遠程伺服器,通過HTTP載入原始位元組碼檔案,然後在Java虛擬機器中轉化為Java類。當然類載入器還做了其他的很多事情:他們阻止不安全的Java類,而且保持不同頁面上的不同小程式不會互相干擾。Luke Gorrie寫的一個包Echidna是一個開放的Java軟體包,他允許在一個Java虛擬機器中安全的運行多個Java應用程式。它通過使用自訂類載入器給每個應用程式一份類檔案的拷貝來阻止應用程式之間的幹擾。