類的加載機制-ClassLoader

    ClassLoader :public abstract class ClassLoader  ,  extends Object

    類加載器是負責加載類的對象。ClassLoader 類是一個抽象類。如果給定類的二進制名稱,那麼類加載
會試圖查找出或生成構成類定義數據。一般策略是將名稱轉換爲某個文件,然後從文件系統讀取該名稱的 
“文件”,每個 Class 對象都包含一個對定義它的 ClassLoader 的引用。數組類的Class對象不是由類加載
創建的,而是由Java運行時根據需要自動創建。數組類的類加載器由Class.getClassLoader()返回,該加載
器與其元素類型的類加載器是相同的;如果該元素是基本類型,則該數組類沒有類加載器。

    應用程序需要實現ClassLoader的子類,以擴展Java虛擬機動態加載類的方式。

    類加載器通常由安全管理器使用,用於指示安全域。

    ClassLoader 類使用委託模型來搜索類和資源。每個 ClassLoader 實例都有一個相關的父類加載器。
需要查找類或資源時,ClassLoader 實例會在試圖親自查找類或資源之前,將搜索類或資源的任務委託給其父
類加載器。虛擬機的內置類加載器(稱爲 "bootstrap class loader")本身沒有父類加載器,但是可以將它
用作 ClassLoader 實例的父類加載器。

    通常情況下,Java 虛擬機以與平臺有關的方式,從本地文件系統中加載類。例如,在 UNIX 系統中,虛
擬機從 CLASSPATH 環境變量定義的目錄中加載類。

    然而,有些類可能並非源自一個文件;它們可能源自其他來源(如網絡),也可能是由應用程序構造的。
defineClass 方法將一個 byte 數組轉換爲 Class 類的實例。這種新定義的類的實例可以使用
Class.newInstance 來創建。

    類加載器所創建對象的方法和構造方法可以引用其他類。爲了確定引用的類,Java 虛擬機將調用最初創建
該類的類加載器的 loadClass 方法。

    ClassLoader的類別:
        *啓動類加載器(BootStrap)
        *擴展類加載器(Extension)
        *應用程序類加載器(AppClassLoader)
        *用戶自定義加載器

    一般我們自己寫的類用的都是AppClassLoader類加載器,就是下面的測試結果中,
獲取自己寫的類的類加載器是 sun.misc.Launcher$AppClassLoader@18b4aac2,而獲取String的
就是null,這個null就是BootStrap這個加載器,BootStrap類加載器相當於啓動類加載器、
應用程序類加載器的祖宗,若是用了BootStrap,由於BootStrap上一級已經沒有了,所以就用“null”來表示



                        |——————————————————————|
                        |BootStrap ClassLoader |  $JAVA_HOME/jre/lib/rt.jar
                        |——————————————————————|
                                    |
                        |——————————————————————|
                        |Extension ClassLoader |  $JAVA_HOME/jre/lib/*.jar
                        |——————————————————————|
                                    |
                        |——————————————————————|
                        |System ClassLoader    |  $CLASSPATH
                        |——————————————————————|
                           |              |
                          |                |
      |————————————————————————|        |————————————————————————|
      |User-Defined ClassLoader|        |User-Defined ClassLoader|
      |————————————————————————|        |————————————————————————|
                  |
                  |
      |————————————————————————|
      |User-Defined ClassLoader|
      |————————————————————————|

    這張圖就可以很清晰得看到:
1.所有在$Java_Home/jre/lib/rt.jar是通過BootStrap加載的
2.所有在$Java_Home/jre/lib/ext/*.jar是通過Extension加載的
3.所有在$CLASSPATH是通過SYSTEM加載的(應用程序類加載器也叫系統類加載器,加載當前應用的classpath 
  的所有類)


舉個例子:創建一個java.lang包,然後創建String類,打印一句話執行

package java.lang; 
public class String{ 
    public static void main(String[] args){
        System.out.println("Hello World");
    }
}

    執行後報錯:錯誤:在類java.lang.String中找不到main方法,請將main方法定義爲public static
 void main(String[] args),否則JavaFX應用程序類必須擴展javafx.application.Application

    出現這種情況,涉及到雙親委派機制:

    當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父類去完成,
每一個層次類加載器都是如此,因此所有的加載請求都應該傳送到啓動類加載器中,只有當父類加載器反饋自己
無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的Class),子類加載器纔會嘗試自己去加載。
 
    所以他的實際運行過程是這樣的:
ClassLoader收到String類的加載請求,先去BootStrap類中查找是否有這個類,沒有則反饋無法完成這個請
求,但是在rt.jar中找到了java.lang.String這個類,執行這個類,這個類中沒有定義main方法
    
    所以上面的例子,他會找到jdk中java.lang.String這個類,這個類確實沒有main方法,簡單說它執行了
的類是JDK中java.lang.String這個類,而不是我們自己定義的類

    雙親委派機制的優點:採用雙親委派的一個好處是比如加載位於 rt.jar 包中的類 java.lang.Object,
不管是哪個加載器加載這個類,最終都是委託給頂層的啓動類加載器進行加載,這樣就保證了使用不同的類加載
器最終得到的都是同樣一個 Object對象。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章