java中一個類是如何被虛拟機加載的呢?下面我們來介紹一下類加載器和雙親委派機制。
類加載器(ClassLoader)
"通過一個類的全限定名來擷取描述此類的二進制位元組流"這個動作是在JVM外部來實作的,以便讓應用程式自己決定如何擷取所需的類.實作這個動作的代碼子產品就稱為類加載器.
ClassLoader隻負責加載class檔案,class檔案在檔案的開頭都有特定的檔案辨別,至于它是否可以運作,則由Execution Engine決定.
類加載器結構
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL1smaNFTQU5keNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL2ADN0MTN1kDM5ADMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
類加載器種類
類加載器分為虛拟機自帶的加載器(前3種)和使用者自定義的加載器(第4種)
1.啟動類加載器(BootstrapClassLoader)
負責加載%JAVA_HOME%\lib下的rt.jar、charsets.jar和class等,或者是-Xbootclasspath參數指定的路徑.根類加載器可以了解成一個概念上的東西,因為我們無法通過Java代碼擷取根類加載器,它屬于JVM層面.
2.擴充類加載器(ExtClassLoader)
該類是sun.misc.Launcher的一個内部類.負責加載%JAVA_HOME%\lib\ext目錄下的所有jar包和class檔案,或者是java.ext.dirs參數指定的路徑.
3.應用程式類加載器(AppClassLoader)
該類是sun.misc.Launcher的一個内部類.負責加載使用者類路徑上所指定的類庫(主要負責加載應用程式的主函數類),如果應用程式中沒有自定義加載器,那麼類加載器就為預設加載器.
4.使用者自定義的加載器
需要繼承ClassLoader抽象類
說明:
擴充類加載器和應用類加載器都是通過類sun.misc.Launcher進行初始化,而Launcher類則由根類加載器進行加載.
雙親委派機制
虛拟機是根據類的全限定名來加載類的,那麼有個問題,如果同時存在兩個或多個全限定名完全一緻的情況下.如何選擇加載哪個類.這就是雙親委派機制要做的工作.
雙親委派機制的原理
雙親委派模型是指當我們調用類加載器的loadClass()進行類加載時,該類加載器會首先請求它的父類加載器進行加載,依次遞歸.如果所有父類加載器都加載失敗,則目前類加載器自己進行加載操作.
1-類加載器收到類加載的請求;
2-把這個請求委托給父加載器去完成,一直向上委托直到啟動類加載器;
3-啟動器加載器檢查能不能加載(使用findClass()方法),能就加載(結束);否則,抛出異常,通知子加載器進行加載.
4-重複步驟三.
說明:
當一個HelloWorld.class檔案被加載時.不考慮自定義類加載器,首先會在AppClassLoader中檢查是否已加載過,如果有就無需再加載.如果沒有,那麼會拿到父加載器,然後調用父加載器的loadClass().父類中同樣會先檢查自己是否已經加載過,如果沒有再往上.直到到達BootstrapClassLoader之前,都是沒有哪個加載器自己選擇加載的.如果父加載器無法加載,會下沉到子加載器去加載,一直到最底層,如果沒有任何加載器能加載,就會抛出ClassNotFoundException.
雙親委派機制設計的優點
1:安全,可避免使用者自己編寫的類動态替換Java的核心類,如java.lang.String
2:避免全限定命名的類重複加載(使用了findLoadClass()判斷目前類是否已加載)
這種設計有個好處是,如果有人想替換系統級别的類:String.在雙親委派機制下這些系統類已經被Bootstrap classLoader加載過了,不會再去加載,從一定程度上防止了危險代碼的植入.
注意:
1.ClassLoader類是一個抽象類,但沒有包含任何抽象方法.
2.如果要實作自己的類加載器且不破壞雙親委派模型,隻需繼承ClassLoader類并重寫findClass().
3.如果要實作自己的類加載器且破壞雙親委派模型,則需繼承ClassLoader類并重寫loadClass()、findClass().