咱們都知道。類加載器用來把類加載到java虛擬機。從JDK2.0開始,類的加載過程採用父親委託機制。JVM的ClassLoader採用的是樹形結構,除了根類加載器之外,每一個ClassLoader都會有且僅有一個父類加載器,用戶自定義的ClassLoader默認的父類加載器是系統類加載器,固然你能夠本身指定須要用個ClassLoader的實例,咱們來看他們的父子關係:java
父類委託機制中,當一個java程序請求加載器loader1加載Hello類時,loader1首先委託本身的父親加載器加載hello類,若父親加載器能加載,則由附加器完成加載人物,不然才由加載器loader1自己加載Hello類。下面咱們來再次看一下java虛擬機自帶的幾個加載器:安全
除了java虛擬機自帶的加載器以外,咱們用戶本身也能夠自定義本身的類加載器,根據本身的須要。。Java提供了抽象類java.lang.ClassLoder,全部用戶自定義的類加載器都要繼承這個classloader類。spa
注:加載器之間的父子關係實際上指的是加載器對象之間的包裝關係,而不是類之間的繼承關係。一對父子加載器多是同一個加載器類的兩個實例,也可能不是。在子加載器對象中包裝了一個父加載器對象.當生成一個自定義的類加載器實例時,若是沒有指定它的父加載器,那麼系統類加載器就將成爲該類加載器的父加載器。若是在構造方法中指定父類加載器那麼父類加載器就是指定的加載器。證實以下:
code
ClassLoader loader1 = new MyClassLoader(); //參數loader1將做爲loader2的父加載器 ClassLoader loader2 = new MyClassLoader(loader1);
當Java虛擬機要加載一個類時,到底該派哪一個類加載器去加載呢?咱們看下圖:orm
loader1和loader2是咱們本身定義的兩個類加載器,loader1和loader2是父子關係。如今咱們想讓loader2這個類加載器加載咱們本身寫的一個Sample類:對象
loader2.loadclass("sample");
咱們來分析一下看看到底應該用哪個類加載器去加載。當這段代碼被執行時,loader2首先到本身的命名空間去查找Sample類是否已經被加載,若是被加載就直接返回這個類的class對象的引用。若是Sample類尚未被加載,loader2首先請求loader1代爲加載,loader1再請求系統類加載器代爲加載,系統類加載器再請求擴展類加載器,擴展類加載器再請求根類加載器,若根類加載器和擴展類加載器都不能加載,則系統類加載器嘗試加載,若能加載,則將Sample類所對應的Class對象的引用返回給loader1,loader1在將引用返回給loader2,從而成功將Sample類加載到虛擬機。若系統類加載器不能加載Sample類,則loader1嘗試加載Sample了哦,若loader1不能加載,則loader2嘗試,若全部的類加載都不能加載,則拋出ClassNotFoundException異常。
繼承
定義類加載器:若是某個類加載器可以加載一個類,那麼該類加載器就稱做:定義類加載器;定義類加載器及其全部子加載器都稱做:初始類加載器。ssl
父委託機制的優勢就是可以提升軟件系統的安全性。由於在詞機制下,用戶自定義的類加載器不可能加載本應該由父加載器加載的可靠類,從而防止不可靠的惡意代碼代替由父類加載器加載的可靠類,從而防止不可靠的甚至惡意的代碼代替由父類加載器加載的可靠代碼。如,java.lang.Object類老是由根類加載器加載的,其餘任何用戶自定義的類加載器都不可能加載含有惡意代碼的java.lang.Object類。 虛擬機
命名空間,其實這裏所說的命名空間就是咱們java中經常使用的package,每一個類加載器都有本身的命名空間,命名空間由該加載器及全部父加載器所加載的類的組成。在同一個命名空間中,不會出現類的完整名字(包括類的包名)相同的兩個類;在不一樣的命名空間中,有可能會出現類的完整名字(包括類的包名)相同的兩個類。
it
由同一類加載器加載的屬於相同包的類組成了運行時包。決定兩個類是否是屬於同一個運行時包,不只要看他們的包名稱是否相同,還要看定義類加載器是否相同。只有屬於同一運行時包的類之間才能相互訪問可見(默認訪問級別)的類和成員。假設用戶自定義了一個類java.lang.TestCase並由用於自定義的類加載器加載,因爲java.lang.TestCase和核心類庫java.lang.*由不一樣的類加載器加載,他們屬於不一樣的運行時包,因此java.lang.TestCase不能訪問核心庫java.lang包中的包可見成員。