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

 

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

 

類加載器

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

 

類加載器的類型

  有兩種類型的類加載器:安全

  1.JVM自帶的加載器:學習

    根類加載器(Bootstrap)spa

    擴展類加載器(Extension)操作系統

    系統類加載器(System)視頻

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

    java.lang.ClassLoader的子類,用戶能夠定製類的加載方式。blog

 

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系列視頻教程。

相關文章
相關標籤/搜索