Java類加載器和雙親委派機制

前言

以前詳細介紹了Java類的整個加載過程(類加載機制詳解)。雖然,篇幅較長,可是也不要被內容嚇到了,其實每一個階段均可以用一句話來歸納。html

1)加載:查找並加載類的二進制字節流數據。java

2)驗證:保證被加載的類的正確性。面試

3)準備:爲類的靜態變量分配內存,並設置默認初始值。安全

4)解析:把類中的符號引用轉換爲直接引用。函數

5)初始化:爲類的靜態變量賦予正確的初始值。spa

固然,要想掌握類加載機制,仍是須要去深刻研究的。(好吧,說了一句正確的廢話)由於其中,有不少知識點也是面試中常問的。好比,我以前去面試的時候,面試官就問到了一個和類初始化相關的問題。就是給一段代碼,有父子類關係,父子類中包含靜態代碼塊、構造代碼塊、普通代碼塊、構造函數等,而後讓判斷代碼最終的執行順序。(可自行思考一下,具體內容細節暫時不作擴展).net

類加載器

終於來到了本文的主題 —— 類加載器和雙親委派機制。設計

在《深刻理解Java虛擬機》中,對於類加載器的定義是這樣的:code

虛擬機設計團隊把類加載階段中的「經過一個類的權限定名來獲取描述此類的二進制字節流」這個動做放到Java虛擬機外部去實現,以便讓應用程序本身決定如何去獲取所須要的類。實現這個動做的代碼模塊稱爲「類加載器」。htm

簡單來講,類加載器的做用就是去加載class類的二進制字節流的。

類加載器有如下三種:

1)啓動類加載器(Bootstrap ClassLoader),或者叫根加載器。這個類加載器主要是去加載你在本機配置的環境變量 Java_Home/jre/lib 目錄下的核心API,如rt.jar

file

2)擴展類加載器(Extension ClassLoader)。這個加載器負責加載 Java_Home/jre/lib/ext 目錄下的全部jar包。

3)應用程序類加載器(Application ClassLoader)。這個加載器加載的是你的項目工程的ClassPath目錄下的類庫。若是用戶沒有自定義本身的類加載器,這個就是程序默認的類加載器。

另外,若是有須要的話,用戶也能夠自定義本身的類加載器(去繼承ClassLoader類)。

咱們也能夠經過代碼把類加載器打印出來:

public class TestClassLoader {
    public static void main(String[] args) {
        Object obj = new Object();
        System.out.println(obj.getClass().getClassLoader());

        TestClassLoader t = new TestClassLoader();
        System.out.println(t.getClass().getClassLoader());
        System.out.println(t.getClass().getClassLoader().getParent());
        System.out.println(t.getClass().getClassLoader().getParent().getParent());
    }
}

打印結果:

null
sun.misc.Launcher$AppClassLoader@58644d46
sun.misc.Launcher$ExtClassLoader@6d6f6e28
null

注意,上面第一行和第四行的null此處可不是空的意思,它表明的是啓動類加載器。由於啓動類加載器是用C++代碼來實現的,嚴格來講不屬於Java類,因此Java代碼訪問不到,故返回null。第二行是應用程序類加載器,第三行是擴展類加載器。

雙親委派機制

在介紹雙親委派機制以前,先觀察一下如下代碼可否正確運行:

//本身定義的一個 java.lang包
package java.lang;

public class String {
    public static void main(String[] args) {
        String s = new String();
        System.out.println(s);
    }
}

以上代碼,編譯沒有任何問題,可是運行時,卻報錯:

file

爲何提示在java.lang.String類中找不到main方法呢,我這明明不是定義了嗎?其實,問題的關鍵就在於類加載遵循雙親委派機制。

類加載器有如下這樣的層次關係:

file

當一個類在加載的時候,都會先委派它的父加載器去加載,這樣一層層的向上委派,直到最頂層的啓動類加載器。若是頂層沒法加載(即找不到對應的類),就會一層層的向下查找,直到找到爲止。 這就是類的雙親委派機制。

這樣作有什麼好處呢?這就至關於維護了一個有優先級的層級關係,即老是從最頂層的父加載器開始加載。這就如同,你工做中遇到了問題須要向上反饋,好比先反饋給小組長,而後小組長反饋給上級經理,最後經理反饋給boss。而後boss感受這問題太簡單了不須要他親自出手,讓經理本身解決吧,而後經理又向下交給小組長。小組長一看,這問題不算難,人也比較熱心,因而就幫你把問題解決了。(可能例子不是太恰當哈,意思理解便可)

到此,咱們就明白了爲何上邊的代碼會報錯。由於雙親委派機制的存在,去加載咱們本身定義的「java.lang.String」類的時候,會最終委派到頂層的啓動類加載器,而後找到了rt.jar包下的「java.lang.String」。找到以後,就直接加載rt.jar包的String類(也就是咱們常用的那個字符串類),再也不去向下查找,也就加載不了咱們自定義的String類了。因爲,rt.jar包下的String類中確實沒有main方法,因此纔會有以上的報錯信息。

咱們能夠試想一下,若是沒有雙親委派機制的存在,那我這段代碼是否是就能夠執行成功了。若是這樣的話,豈不是說明我能夠隨意覆蓋rt.jar包中的類(如String,Integer類等)。這樣的話將會使程序陷入混亂,Java核心包中的類的安全也沒法保證。

原文出處:https://www.cnblogs.com/starry-skys/p/12299803.html

相關文章
相關標籤/搜索