歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java類加載器

Java類加載器

日期:2017/3/1 9:50:00   编辑:Linux編程

一、 類加載器作用

當JVM開始運作時需要使用當某個類時,就需要將對應類的字段嗎加載到內存中,而類加載器正式負責加載這些類的工具。另外若果多次重復使用一個類的字節碼時加載器不會多次加載,而是使用內存中的字節碼。

二、 主要的類加載器

我們首先看一下JVM預定義的三種類型類加載器,當一個 JVM 啟動的時候,Java 缺省開始使用如下三種類型類裝入器

1. 引導(Bootstrap)類加載器:引導類裝入器是用本地代碼實現的類裝入器,它負責將JRE/lib/tr.jar加載到內存中。由於引導類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啟動類加載器的引用,所以不允許直接通過引用進行操作。

2. 標准擴展(Extension)類加載器:擴展類加載器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 實現的。它負責將JRE/lib/ext/*.jar或者由系統變量 java.ext.dir 指定位置中的類庫加載到內存中。開發者可以直接使用標准擴展類加載器。

3. 系統(System)類加載器:系統類加載器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的。它負責將系統類路徑(CLASSPATH)中指定的類庫加載到內存中。開發者可以直接使用系統類加載器。

三個類加載器之間的繼承關系

BootStrap——負責加載JRE/lib/tr.jar

/|\

ExtClassLoader——負責加載JRE/lib/ext/*.jar

/|\

AppClassLoader——負責加載CLASSPATH所指定的所有jar或目錄(SystemClassLoader)

說明

每個加載器也是一個類,也需要被加載器加載,顯然需要有一個最頂級加載器,而且它不需要被加載,這個加載器就是BootStrap加載器,它不是一個類,而是貯存在JVM內核中的一段C++代碼,JVM啟動會自動執行這段代碼。BootStrap加載器負責JRE/lib/tr.jar中的類,當然包括其他加載器。

三、 加載器委托加載機制

在加載類時,當前的類加載器(發起者)首先讓其父類去加載,其父類加載器又讓父類加載器加載,以此類推,直到提交到最頂級類加載器,即BootStrap加載器為止,這時BootStrap加載器開始加載,若找不到該類,則返回給要求它加載的加載器去加載,直到加載到,若發起者(最先請求加載的類加載器)也找不到該類,則拋出ClassNotFoundException。

四、 類加載器中一些重要方法

方法 loadClass

ClassLoader.loadClass()是ClassLoader的入口點,程序通過調用該方法進行類的加載。方法簽名如下:Class loadClass( String name, boolean resolve);

參數name指定Java虛擬機需要的類的全名(含包名),比如Foo或者java.lang.Object。

參數 resolve指定該類是否需要解析你可以把類的解析理解為完全為運行做好准備。解析一般都不需要。如果Java虛擬機只想知道這個類是否存在或者想知道它的父類的話,解析就完全沒有必要了。 在Java1.1和它以前的版本,如果要自定義類加載器,loadClass方法是唯一需要在子類中覆蓋的方法.(ClassLoader在Java1.2中有所改變,提供了方法findClass())。

方法findClass()

Java1.2之後自定義類加載器,除了要繼承ClassLoader外,只需要覆寫該方法即可。在調用loadClass()時,如果父類加載器找不到,會調用該類加載器的findClass()進行查找類。

方法 defineClass

defineClass 是ClassLoader中一個很神秘的方法。這個方法通過一個字節數組來構建類實例。這個包含數據的原始字節數組可能來自文件系統,也可能是來自網絡。defineClass 表

明了Java虛擬機的復雜性,神秘性和平台依賴性-它通過解釋字節碼把它轉化為運行時數據

結構,檢查有效性等等。但是不用擔心,這些都不用你去實現。其實,你根本不能覆蓋它,

因為該方法被關鍵字final修飾。

方法 findSystemClass

findSystemClass方法從本地系統加載文件。它在本地系統尋找類文件,如果找到了,調用

defineClass把原始字節數組轉化成類對象。這是運行Java應用時Java虛擬機加載類的默認機制。對於自定義類加載器,只有在我們無法加載之後才需要用findSystemClass。 原因很簡單: 我們的類加載器負責執行類加載中的某些特定的步驟,但並不是對所有的類。比如,即使我們的類加載器從遠程站點加載了某些類,仍然有很多基本的類要從本地系統加載。

這些類不是我們關心的,所以我們讓Java虛擬機以默認的方式加載他們:從本地系統。這就是findSystemClass做的事情。整個過程大致如下:

* Java虛擬機請求我們自定義的類加載器加載類。

* 我們檢查遠程站點是否有這個需要加載的類。

* 如果有,我們獲取這個類。

* 如果沒有,我們認為這個是類在基本類庫中,調用findSystemClass從文件系統中加載。

在大多數自定義類加載器中,你應該先調用findSystemClass來節省從遠程查找的時間。

實際上,正如我們將在下一部分看到的,只有當我們確定我們已經自動編譯完我們的代碼後

才允許Java虛擬機從本地文件系統加載類。

方法resolveClass

正如上面說的,類記載可以分為部分加載(不解析)和完全加載(包括解析)。我們創建自

定義類加載器的時候,可能要調用resolveClass。

方法 findLoadedClass

findLoadedClass實現一個緩存:當要求loadClass來加載一個類的時候,可以先調用這個方法看看這個類是否已經被加載,防止重新加載一個已經被加載的類。這個方法必須先被調用,我們看一下這些方法是如何組織在一起的。

我們的例子實現loadClass執行以下的步驟。(我們不指定通過某種具體的技術獲得類文件,-它可能從網絡,從壓縮包或者動態編譯的。無論如何,我們獲得的是原始字節碼文件)

1. 程序調用該類加載器的loadClass()方法。

2. loadClass()內部調用findLoadedClass檢查這個類是否已經加載。

3. 如果沒有加載,調用父類加載器(父類再調用父類)加載,加載到,返回這個類。

4. 否則,調用findCLass()方法,加載類。具體代碼在findClass()中,執行查找文件,讀取,編譯等操作,生成字節碼,返回。

5. 如果參數resolve為true,調用resolveClass來解析類對象。

6. 如果還沒有找到類,拋出一個ClassNotFoundException異常。

現在我們看一下ClassLoader中的loadClass()源碼

protected synchronized Class<?> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

// First, check if the class has alreadybeen loaded

Class c = findLoadedClass(name);

if (c == null) {

try {

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c =findBootstrapClassOrNull(name);

}

} catch(ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

if (c == null) {

// If still not found, then invoke findClass in order

// to find the class.

c =findClass(name);

}

}

大致這是這樣。

五、 用戶自定義類加載器

通過前面的分析,我們可以看出,要想實現自定義類加載器,除需要繼承ClassLoader之外,還有就是覆寫findClass()方法,在這裡面寫自己的操作。

一般用戶自定義類加載器的工作流程:

1、首先檢查請求的類型是否已經被這個類裝載器裝載到命名空間中了,如果已經裝載,直接返回;否則轉入步驟2

2、委派類加載請求給父類加載器(更准確的說應該是雙親類加載器,整個虛擬機中各種類加載器最終會呈現樹狀結構),如果父類加載器能夠完成,則返回父類加載器加載的Class實例;否則轉入步驟3

3、調用本類加載器的findClass(…)方法,試圖獲取對應的字節碼,如果獲取的到,則調用defineClass(…)導入類型到方法區;如果獲取不到對應的字節碼或者其他原因失敗,返回異常給loadClass(…), loadClass(…)轉拋異常,終止加載過程(注意:這裡的異常種類不止一種)。

(說明:這裡說的自定義類加載器是指JDK1.2��後版本的寫法,即不覆寫改變java.lang.loadClass(…)已有委派邏輯情況下)

具體就不舉例子了!

Copyright © Linux教程網 All Rights Reserved