Java類加載器

Java類加載器

java虛擬機中可以安裝多個類加載器,系統默認的有3個主要的類加載器。
每一個類加載器負責加載對應位置的類。
BootStrap、ExtClassLoader、AppClassLoader
注:類加載器也是java類,當然也會被類加載器所加載,才能夠被使用,因此有一個特殊的類加載器就是:BootStrap
BootStrap使用C++寫的,在底層去加載java類。因此他不屬於ClassLoader類型

一、Java類加載器的結構

1、那麼類加載器的結構是什麼呢,用以下例子說明:

public static void main(String[] args){
    // TODO Auto-generated method stub
    ClassLoader loader=ClassLoaderTest.class.getClassLoader();//獲取本類的類加載器
    //用循環來獲取類加載器的父加載器
    while(loader!=null){
        System.out.println(loader);
        loader=loader.getParent();
    }
}

結果爲:

sun.misc.Launcher$AppClassLoader@1372a1a  

sun.misc.Launcher$ExtClassLoader@ad3ba4

根據結果可以看出:
ClassLoaderTest這個類的加載器是AppClassLoader,也就是說AppClassLoader加載的是設置的classpath下的類;AppClassLoader這個類的父加載器是ExtClassLoader。

2、如果把ClassLoaderTest這個類導出爲jar包,放入jre/lib/ext文件目錄下,又會怎麼樣呢?

這裏寫代碼片則運行結果變成了如下:

sun.misc.Launcher$ExtClassLoader@ad3ba4

根據結果可以看出:
ExtClassLoader這個加載器負責加載ext文件裏jar包的類;如果同時classpath下也有這個類,AppClassLoader將不會加載,這體現出了類加載器的委託機制。

所謂類加載器的委託機制:在類要加載的時候,默認是用當前類所在的線程中的加載器去加載,這個加載器將會自動的先去委託他的父加載器去找,依次類推。自然會找到最上級,由最上級去找他負責的類加載區,如果有,則加載;如果沒有,則再去讓下一級找…直到由這個類所在線程中的類加載器去加載,如果還是沒有,就會拋出異常:ClassNotFoundException。

這樣做的好處:如果在類加載器指定的目錄下同時存在相同的類,那麼這種機制可以保證從最頂層的類加載器開始加載,不用每一個載器都去加載,這樣就保證了加載完生成字節碼的唯一性,節省了內存空間;減少了加載次數,從而提高了效率。

3、分析三種類加載器的使用場合:

(1)、系統類加載器(應用類加載器)
這個加載器使用java實現,使用廣泛,負責加載classPath中指定的類。具體的使用場合是:加載classPath中指定的而擴展類加載器沒有加載的類。若擴展類加載器加載了classPath中的類,則系統類加載器則沒有機會加載。
用戶定義的類一般都是系統類加載器加載的。
可以通過:ClassLoader.getSystemClassLoader()獲得。
(2)、擴展類加載器
它負責加載Java的標準擴展,一般使用Java實現的,負責加載jre/lib/ext中的類。和普通的類加載器一樣。
可以通過:ClassLoader.getSystemClassLoader().getParent()獲得。
(3)、引導類加載器。
它負責加載jdk中的系統類,是用C/C++語言實現的。對於java程序無法獲得它,像上文中獲得擴展類加載器的父類加載器是null。像String,Integer,Double類都是由引導類加載器加載的。

二、Java類的加載過程

JVM將類加載過程分爲三個步驟:裝載(Load),鏈接(Link)和初始化(Initialize)鏈接又分爲三個步驟,如下圖所示:

這裏寫圖片描述

1、 裝載:查找並加載類的二進制數據;

2、鏈接

驗證:確保被加載類的正確性;
準備:爲類的靜態變量分配內存,並將其初始化爲默認值;
解析:把類中的符號引用轉換爲直接引用;

3、初始化:爲類的靜態變量賦予正確的初始值;

那爲什麼要有驗證這一步驟呢?
首先如果由編譯器生成的class文件,它肯定是符合JVM字節碼格式的,但是萬一有高手自己寫一個class文件,讓JVM加載並運行,用於惡意用途,就不妙了,因此這個class文件要先過驗證這一關,不符合的話不會讓它繼續執行的,也是爲了安全考慮吧。
準備階段和初始化階段看似有點牟盾,其實是不牟盾的,如果類中有語句:private static int a = 10,它的執行過程是這樣的,首先字節碼文件被加載到內存後,先進行鏈接的驗證這一步驟,驗證通過後準備階段,給a分配內存,因爲變量a是static的,所以此時a等於int類型的默認初始值0,即a=0,然後到解析(後面在說),到初始化這一步驟時,才把a的真正的值10賦給a,此時a=10。

三、類的初始化

類什麼時候才被初始化:

1、創建類的實例,也就是new一個對象
2、訪問某個類或接口的靜態變量,或者對該靜態變量賦值
3、調用類的靜態方法
4、反射(Class.forName(“com.lyj.load”))
5、初始化一個類的子類(會首先初始化子類的父類)
6、JVM啓動時標明的啓動類,即文件名和類名相同的那個類

只有這6中情況纔會導致類的類的初始化。

類的初始化步驟:

1、如果這個類還沒有被加載和鏈接,那先進行加載和鏈接

2、假如這個類存在直接父類,並且這個類還沒有被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用於接口)

3、加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。

四、類的加載

類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然後在堆區創建一個這個類的Java.lang.Class對象,用來封裝類在方法區類的對象。如下圖

這裏寫圖片描述

類的加載的最終產品是位於堆區中的Class對象

Class對象封裝了類在方法區內的數據結構,並且向Java程序員提供了訪問方法區內的數據結構的接口

加載類的方式有以下幾種:
1、從本地系統直接加載
2、通過網絡下載.class文件
3、從zip,jar等歸檔文件中加載.class文件
4、從專有數據庫中提取.class文件
5、將Java源文件動態編譯爲.class文件(服務器)

發佈了61 篇原創文章 · 獲贊 7 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章