1. 程式人生 > >"黑馬程式設計師"類載入器及其委託機制

"黑馬程式設計師"類載入器及其委託機制

------ <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">java培訓</a>、期待與您交流! ----------

類載入器分析

簡說:類載入器就是載入類的工具。

當出現一個類,要用此類時,那麼Java虛擬機器首先會將位元組碼載入進記憶體,位元組碼的原始檔是放在硬碟上的classpath指定的目錄下。

類載入器 的作用:將.class檔案中的內容載入進記憶體進行處理,處理完後為位元組碼。

預設的類載入器:

JAVA虛擬機器中可以安裝多個類載入器,系統預設的是三個類載入器,每個類載入器載入特定位置的類:BootStrap、ExtClassLoader、AppClassLoader

BootStrap--為頂級類載入器:

類載入器本身就是一個特殊的java類,所以本身也是需要載入器載入,顯然必須有第一個類載入不為java類,這個就是BootStrap。她是一個用C++寫的一串二進位制程式碼巢狀在java虛擬機器核心中,即一啟動就出現在虛擬機器中。所以不能通過java程式獲取到它的名字,獲得的只能為null值。

Java虛擬機器中的所有類載入器採用子父關係的樹形結構進行組織,在例項化沒有類載入器物件時,需要為其指定一個父級類裝載器物件或者預設採用系統類載入器為其父級載入。

如圖:


小例:

ClassLoader loader = ClassLoaderTest.class.getClassLoader();

while(loader!=null){

System.out.println(loader.getClass().getName());

loader = loader.getParent();//向上遍歷,父類賦值給子類

}

System.out.println(loader);

輸出結果:

sun.misc.Launcher$AppClassLoader

sun.misc.Launcher$ExtClassLoader

null

所以BootStarp為頂層父類,ExtClassLoader是BootStrap類的子類,ExtClassLoader又是AppClassLoader的父類。

我們在安裝JDK後,在JRE/lib的目錄下有一個rt.jar檔案,該jar包是系統通過的。在lib目錄下面還有一個ext目錄,裡面有擴充套件功能的jar包。ExtClassLoader類載入器載入的是ext目錄下的jar包。

在ext目錄和classpath目錄下都有class檔案,優先載入父類的,如果父類中沒有再載入子類中的jar檔案。這就是傳說中的載入器的委託機制。

類載入委託機制

當Java虛擬機器載入一個類時,到底是用的那個類載入器去載入雷的呢?

當前執行緒的類載入器去載入執行緒的第一個類。

如果A類中引用了B類,Java虛擬機器將使用載入A的載入器去載入B。

還可以直接呼叫ClassLoader.loadClass()方法來指定某個類載入器去載入某個類。

每個類載入器 載入類時,又先委託給其上級類載入器。

當所有的類載入器沒有載入到類,九返回到發起者載入器,還載入不到就會丟擲ClassNotFoundExcption,而不會找發起者的子類。

其中有一道面試題,問能否自定義個名為java.lang.System的類?

通過以上可得出總結,一般是不可用的。因為載入器委託機制的原理決定的。為了不讓我們寫System類,類載入器採用委託機制,這樣就可以保證父類優先,也就是總是使用父類能夠找到的類,這樣總是使用java系統提供的System類。

但是還是有辦法載入這個自定義的System類的,此時就不能交給上級載入了,需要用自定義的類載入器載入,這就需要有特殊的寫法才能去載入這個自定義的System類的。

自定義載入器

1.自定義載入器必須繼承抽象類ClassLoader,要覆蓋其中的findClass(String name)方法,而不用腹瀉loadClass()方法。

2.覆寫findClass(String name)方法的原因:

1)是要保留loadClass()方法中的流程,因為loadClass()中呼叫了findClass(String name)這個方法,此方法返回的就是去尋找父級的類載入器。

2)在loadClass()內部是會先委託給父級,當父級找到後就會呼叫findClass(String name)這個方法,而找不到時就會用自己的類載入器,再找不到就報異常了,所以只需要覆寫findClass()方法,那麼就具有了實現用自定義的類載入器載入類的目的 。

流程:

父級-->loadClass-->findClass-->得到Class檔案後轉化成位元組碼-->defind().

3.程式設計步驟:

1)編寫一個檔案內容進行簡單加密的程式

2)編寫好了一個自己的類載入器,可實現對加密過來的類進行裝在和解密。

3)編寫一個程式,呼叫類載入器載入類,在源程式中不能用該類名定義引用變數,因為編譯器無法識別這個類,程式中除了可以使用ClassLoader的load方法外,還能使用放置執行緒的上線文類載入器載入或系統類載入器,然後在使用forName得到位元組碼檔案。

編寫對class檔案進行加密的工具類

建立一個加密類,加密類中有對檔案的加密方法,對加密過的class檔案也可以使用該加密方法進行解密:

public class MyClassLoader {
    public static void main(String[] args)throws IOException {
       //讀取class檔案,加密方法會對該class檔案進行加密
       FileInputStream fis = new FileInputStream("D:\\Eclipse\\workspace\\Itheima\\bin\\ClassLoaderAttachment.class");
       FileOutputStream fos = new  FileOutputStream("D:\\Eclipse\\workspace\\Itheima\\itcastlib\\ClassLoaderAttachment.class");
       cypher(fis, fos);
       fis.close();
       fos.close();
    }
    //加密與解密方法
    public static void cypher(InputStream in, OutputStream out)throws IOException {
       int b = -1;
       while((b = in.read()) != -1) {
           //一個位元組資料與0xFF進行異或運算,也就是將二進位制資料取反
           out.write(b ^ 0xFF);
       }
    }
}