Java虛擬機JVM學習05 類加載器的父委托機制


 

Java虛擬機JVM學習05 類加載器的父委托機制

 

類加載器

  類加載器用來把類加載到Java虛擬機中。

 

類加載器的類型

  有兩種類型的類加載器:

  1.JVM自帶的加載器:

    根類加載器(Bootstrap)

    擴展類加載器(Extension)

    系統類加載器(System)

  2.用戶自定義的類加載器:

    java.lang.ClassLoader的子類,用戶可以定制類的加載方式。

 

JVM自帶的加載器

  Java虛擬機自帶了以下幾種加載器。

  1.根(Bootstrap)類加載器:

  該加載器沒有父加載器

  它負責加載虛擬機的核心類庫,如java.lang.*等。

  根類加載器從系統屬性sun.boot.class.path所指定的目錄中加載類庫。

  根類加載器的實現依賴於底層操作系統,屬於虛擬機的實現的一部分,它並沒有繼承java.lang.ClassLoader類,它是用C++寫的。

 

  2.擴展(Extension)類加載器:

  它的父加載器為根類加載器。

  它從java.ext.dirs系統屬性所指定的目錄中加載類庫,或者從JDK的安裝目錄的jre\lib\ext子目錄(擴展目錄)下加載類庫,如果把用戶創建的JAR文件放在這個目錄下,也會自動由擴展類加載器加載。

  擴展類加載器是純Java類,是java.lang.ClassLoader類的子類。

 

  3.系統(System)類加載器:

  也稱為應用類加載器,它的父加載器為擴展類加載器

  它從環境變量classpath或者系統屬性java.class.path所指定的目錄中加載類,它是用戶自定義的類加載器的默認父加載器

  系統類加載器是純Java類,是java.lang.ClassLoader類的子類。

 

  注意:這里的父加載器概念並不是指類的繼承關系,子加載器不一定繼承了父加載器(其實是組合的關系)。

 

用戶自定義類加載器

  除了以上虛擬機自帶的類加載器以外,用戶還可以定制自己的類加載器(User-defined Class Loader)。

  Java提供了抽象類java.lang.ClassLoader,所有用戶自定義的類加載器都應該繼承ClassLoader類。

 

類加載的父委托機制

  從JDK 1.2版本開始,類的加載過程采用父親委托機制,這種機制能更好地保證Java平台的安全。

  在父委托機制中,除了Java虛擬機自帶的根類加載器以外,其余的類加載器都有且只有一個父加載器,各個加載器按照父子關系形成了樹形結構。

 

  當Java程序請求加載器loader1加載Sample類時,loader1首先委托自己的父加載器去加載Sample類,若父加載器能加載,則由父加載器完成加載任務,否則才由loader1本身加載Sample類。

 

  說明具體過程的一個例子:

 

 

  loader2首先從自己的命名空間中查找Sample類是否已經被加載,如果已經加載,就直接返回代表Sample類的Class對象的引用。

  如果Sample類還沒有被加載,loader2首先請求loader1代為加載,loader1再請求系統類加載器代為加載,系統類加載器再請求擴展類加載器代為加載,擴展類加載器再請求根類加載器代為加載。

  若根類加載器和擴展類加載器都不能加載,則系統類加載器嘗試加載,若能加載成功,則將Sample類所對應的Class對象的引用返回給loader1,loader1再返回給loader2,從而成功將Sample類加載進虛擬機。

  若系統加載器不能加載Sample類,則loader1嘗試加載Sample類,若loader1也不能成功加載,則loader2嘗試加載。

  若所有的父加載器及loader2本身都不能加載,則拋出ClassNotFoundException異常。

  總結下來就是:

  每個加載器都優先嘗試用父類加載,若父類不能加載則自己嘗試加載;若成功則返回Class對象給子類,若失敗則告訴子類讓子類自己加載。所有都失敗則拋出異常。

 

定義類加載器和初始類加載器

  若有一個類加載器能成功加載Sample類,那么這個類加載器被稱為定義類加載器

  所有能成功返回Class對象的引用的類加載器(包括定義類加載器,即包括定義類加載器和它下面的所有子加載器)都被稱為初始類加載器

  假設loader1實際加載了Sample類,則loader1為Sample類的定義類加載器,loader2和loader1為Sample類的初始類加載器。

 

父子關系

  需要指出的是,加載器之間的父子關系實際上指的是加載器對象之間的包裝關系,而不是類之間的繼承關系。

  一對父子加載器可能是同一個加載器類的兩個實例,也可能不是。

  在子加載器對象中包裝了一個父加載器對象。

  例如loader1和loader2都是MyClassLoader類的實例,並且loader2包裝了loader1,loader1是loader2的父加載器。

  當生成一個自定義的類加載器實例時,如果沒有指定它的父加載器(ClassLoader構造方法無參數),那么系統類加載器就將成為該類加載器的父加載器。

 

父委托機制優點

  父親委托機制的優點是能夠提高軟件系統的安全性

  因為在此機制下,用戶自定義的類加載器不可能加載應該由父加載器加載的可靠類,從而防止不可靠甚至惡意的代碼代替由父加載器加載的可靠代碼。

  例如,java.lang.Object類總是由根類加載器加載,其他任何用戶自定義的類加載器都不可能加載含有惡意代碼的java.lang.Object類。

 

命名空間

  每個類加載器都有自己的命名空間,命名空間由該加載器及所有父加載器所加載的類組成。

  在同一個命名空間中,不會出現類的完整名字(包括類的包名)相同的兩個類。

  在不同的命名空間中,有可能會出現類的完整名字(包括類的包名)相同的兩個類。

運行時包

  由同一類加載器加載的屬於相同包的類組成了運行時包

  決定兩個類是不是屬於同一個運行時包,不僅要看它們的包名是否相同,還要看定義類加載器是否相同。

  只有屬於同一運行時包的類才能互相訪問包可見(即默認訪問級別)的類和類成員。

  這樣的限制能避免用戶自定義的類冒充核心類庫的類,去訪問核心類庫的包可見成員。

  假設用戶自己定義了一個類java.lang.Spy,並由用戶自定義的類加載器加載,由於java.lang.Spy和核心類庫java.lang.*由不同的類加載器加載,它們屬於不同的運行時包,所以java.lang.Spy不能訪問核心類庫java.lang包中的包可見成員。

 

參考資料

  聖思園張龍老師Java SE系列視頻教程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM