1. 程式人生 > >getClass()和getClassLoader()區別 以及ClassLoader詳解及用途(檔案載入,類載入)

getClass()和getClassLoader()區別 以及ClassLoader詳解及用途(檔案載入,類載入)

 1.1 幾個相關概念ClassLoader負責載入系統的所有Resources(Class,檔案,來自網路的位元組流等),通過ClassLoader從而將資源載入JVM  
每個class都有一個reference,指向自己的ClassLoader。Class.getClassLoader()  
array的ClassLoader就是其元素的ClassLoader,若是基本資料型別,則這個array沒有ClassLoader  
1.2 主要方法和工作過程Java1.1及從前版本中,ClassLoader主要方法:  
Class loadClass( String name, boolean resolve ); ClassLoader.loadClass() 是 ClassLoader 的入口點  
defineClass 方法是 ClassLoader 的主要訣竅。該方法接受由原始位元組組成的陣列並把它轉換成 Class 物件。原始陣列包含如從檔案系統或網路裝入的資料。  
findSystemClass 方法從本地檔案系統裝入檔案。它在本地檔案系統中尋找類檔案,如果存在,就使用 defineClass 將原始位元組轉換成 Class 物件,以將該檔案轉換成類。當執行 Java 應用程式時,這是 JVM 正常裝入類的預設機制。  
resolveClass可以不完全地(不帶解析)裝入類,也可以完全地(帶解析)裝入類。當編寫我們自己的 loadClass 時,可以呼叫 resolveClass,這取決於 loadClass 的 resolve 引數的值  
findLoadedClass 充當一個快取:當請求 loadClass 裝入類時,它呼叫該方法來檢視 ClassLoader 是否已裝入這個類,這樣可以避免重新裝入已存在類所造成的麻煩。應首先呼叫該方法  
一般load方法過程如下:  

呼叫 findLoadedClass 來檢視是否存在已裝入的類。  
如果沒有,那麼採用某種特殊的神奇方式來獲取原始位元組。(通過IO從檔案系統,來自網路的位元組流等)  
如果已有原始位元組,呼叫 defineClass 將它們轉換成 Class 物件。  
如果沒有原始位元組,然後呼叫 findSystemClass 檢視是否從本地檔案系統獲取類。  
如果 resolve 引數是 true,那麼呼叫 resolveClass 解析 Class 物件。  
如果還沒有類,返回 ClassNotFoundException。  
否則,將類返回給呼叫程式。  
1.3 委託模型自從JDK1.2以後,ClassLoader做了改進,使用了委託模型,所有系統中的ClassLoader組成一棵樹,ClassLoader在載入類庫時先讓Parent尋找,Parent找不到才自己找。  
JVM在執行時會產生三個ClassLoader,Bootstrap ClassLoader、Extension ClassLoader和App ClassLoader。其中,Bootstrap ClassLoader是用C++編寫的,在Java中看不到它,是null。它用來載入核心類庫,就是在lib下的類庫,Extension ClassLoader載入lib/ext下的類庫,App ClassLoader載入Classpath裡的類庫,三者的關係為:App ClassLoader的Parent是Extension ClassLoader,而Extension ClassLoader的Parent為Bootstrap ClassLoader。載入一個類時,首先BootStrap進行尋找,找不到再由Extension ClassLoader尋找,最後才是App ClassLoader。  

將ClassLoader設計成委託模型的一個重要原因是出於安全考慮,比如在Applet中,如果編寫了一個java.lang.String類並具有破壞性。假如不採用這種委託機制,就會將這個具有破壞性的String載入到了使用者機器上,導致破壞使用者安全。但採用這種委託機制則不會出現這種情況。因為要載入java.lang.String類時,系統最終會由Bootstrap進行載入,這個具有破壞性的String永遠沒有機會載入。  

委託模型還帶來了一些問題,在某些情況下會產生混淆,如下是Tomcat的ClassLoader結構圖:  

                Bootstrap 
                  | 
                System 
                  | 
                Common 
                /     
            Catalina  Shared 
                      /     
                   Webapp1  Webapp2 ... 

由 Common 類裝入器裝入的類決不能(根據名稱)直接訪問由 Web 應用程式裝入的類。使這些類聯絡在一起的唯一方法是通過使用這兩個類集都可見的介面。在這個例子中,就是包含由 Java servlet 實現的 javax.servlet.Servlet。  
如果在lib或者lib/ext等類庫有與應用中同樣的類,那麼應用中的類將無法被載入。通常在jdk新版本出現有類庫移動時會出現問題,例如最初我們使用自己的xml解析器,而在jdk1.4中xml解析器變成標準類庫,load的優先順序也高於我們自己的xml解析器,我們自己的xml解析器永遠無法找到,將可能導致我們的應用無法執行。  

相同的類,不同的ClassLoader,將導致ClassCastException異常  

1.4 執行緒中的ClassLoader每個執行中的執行緒都有一個成員contextClassLoader,用來在執行時動態地載入其它類,可以使用方法Thread.currentThread().setContextClassLoader(...);更改當前執行緒的contextClassLoader,來改變其載入類的行為;也可以通過方法Thread.currentThread().getContextClassLoader()來獲得當前執行緒的ClassLoader。  
實際上,在Java應用中所有程式都執行線上程裡,如果在程式中沒有手工設定過ClassLoader,對於一般的java類如下兩種方法獲得的ClassLoader通常都是同一個  

this.getClass.getClassLoader();  
Thread.currentThread().getContextClassLoader();  
方法一得到的Classloader是靜態的,表明類的載入者是誰;方法二得到的Classloader是動態的,誰執行(某個執行緒),就是那個執行者的Classloader。對於單例模式的類,靜態類等,載入一次後,這個例項會被很多程式(執行緒)呼叫,對於這些類,載入的Classloader和執行執行緒的Classloader通常都不同。  

1.5 Web應用中的ClassLoader回到上面的例子,在Tomcat裡,WebApp的ClassLoader的工作原理有點不同,它先試圖自己載入類(在ContextPath/WEB-INF/...中載入類),如果無法載入,再請求父ClassLoader完成。  
由此可得:  

對於WEB APP執行緒,它的contextClassLoader是WebAppClassLoader  
對於Tomcat Server執行緒,它的contextClassLoader是CatalinaClassLoader  
1.6 獲得ClassLoader的幾種方法可以通過如下3種方法得到ClassLoader  
this.getClass.getClassLoader(); // 使用當前類的ClassLoader  
Thread.currentThread().getContextClassLoader(); // 使用當前執行緒的ClassLoader  
ClassLoader.getSystemClassLoader(); // 使用系統ClassLoader,即系統的入口點所使用的ClassLoader。(注意,system ClassLoader與根ClassLoader並不一樣。JVM下system ClassLoader通常為App ClassLoader)  
1.7 幾種擴充套件應用使用者定製自己的ClassLoader可以實現以下的一些應用  
安全性。類進入JVM之前先經過ClassLoader,所以可以在這邊檢查是否有正確的數字簽名等  
加密。java位元組碼很容易被反編譯,通過定製ClassLoader使得位元組碼先加密防止別人下載後反編譯,這裡的ClassLoader相當於一個動態的解碼器  
歸檔。可能為了節省網路資源,對自己的程式碼做一些特殊的歸檔,然後用定製的ClassLoader來解檔  
自展開程式。把java應用程式編譯成單個可執行類檔案,這個檔案包含壓縮的和加密的類檔案資料,同時有一個固定的ClassLoader,當程式執行時它在記憶體中完全自行解開,無需先安裝  
動態生成。可以生成應用其他還未生成類的類,實時建立整個類並可在任何時刻引入JVM