一.Java有兩種類型的類加載器薪夕。
1.Java虛擬機(jī)自帶的加載器。
①根(Bootstrap)類加載器:它用來加載 Java 的核心庫叭披,如java.lang.*等寥殖,它的父加載器為null玩讳。根類加載器從系統(tǒng)屬性sun.boot.class.path所指定的目錄中加載類涩蜘,根類加載器的實(shí)現(xiàn)依賴于底層操作系統(tǒng)嚼贡,屬于虛擬機(jī)實(shí)現(xiàn)的一部分,是用原生代碼來實(shí)現(xiàn)的同诫,是用C/C++編寫粤策,并不繼承自java.lang.ClassLoader。
②擴(kuò)展(Extension)類加載器: 它的父類加載器為根類加載器误窖,它用來加載 Java 的擴(kuò)展庫叮盘,它從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類,或者從JDK的安裝目錄的jre\lib\ext子目錄(擴(kuò)展目錄)下加載類霹俺。Java 虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫目錄柔吼,如果用戶創(chuàng)建的jar文件放在這個(gè)目錄下,該類加載器在此目錄里面查找并加載 Java 類丙唧,擴(kuò)展類加載器是用純java編寫的愈魏,是java.lang.ClassLoader子類。
③系統(tǒng)(System)類加載器:它也叫應(yīng)用加載器想际,它的父類加載器為擴(kuò)展類加載器培漏,它根據(jù) java 應(yīng)用的類路徑(CLASSPATH)或者系統(tǒng)屬性java.class.path所指定的目錄中來加載 Java 類,他是用戶自定義類加載器的默認(rèn)父加載器胡本,一般來說牌柄,Java應(yīng)用的類都是由它來完成加載,可以通過ClassLoader.getSystemClassLoader()來獲取侧甫。系統(tǒng)類加載器是用純java編寫珊佣,是java.lang.ClassLoader類的子類。
注意:java的系統(tǒng)屬性以及加載目錄代碼測試查找披粟。
2.用戶自定義的類加載器彩扔。
用戶編寫自定義類加載器,是需要直接或間接繼承java.lang.ClassLoader抽象類僻爽。自定義類加載器和jvm自帶加載器(根類加載器虫碉、擴(kuò)展類加載器、系統(tǒng)加載器)唯一區(qū)別是否被虛擬機(jī)默認(rèn)使用胸梆,在不指定父類加載器的情況下敦捧,默認(rèn)采用系統(tǒng)加載器。
二.類加載器的父類委托機(jī)制
JVM在加載類是默認(rèn)采用的是雙親委任機(jī)制碰镜,就是某個(gè)特定的類加載器在接到加載類的請(qǐng)求時(shí)兢卵,首先將加載任務(wù)委托給父類加載器,依次遞歸绪颖,如果父類加載器可以完成類加載任務(wù)秽荤,就成功返回;只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載窃款,如果本身類加載器也不能加載成功课兄,則拋出ClassNotFoundException異常。在父親委托機(jī)制中晨继,各個(gè)加載器按照父子關(guān)系形成了樹形結(jié)構(gòu)烟阐,除了根類加載器以外,其余的類加載器都有且只有一個(gè)父加載器紊扬。
1.JVM自帶類加載器的樹狀組織結(jié)構(gòu)
上述代碼運(yùn)行結(jié)果:
第一個(gè)輸出的是ClassLoaderTree類的類加載器蜒茄,即是系統(tǒng)類加載器,它是sun.misc.Launcher$AppClassLoader類的實(shí)例餐屎;第二個(gè)輸出的是擴(kuò)展類加載器檀葛,是sun.misc.Launcher$ExtClassLoader類的實(shí)例。需要注意的是這里沒有根類加載器腹缩,這是由于有些jdk的實(shí)現(xiàn)對(duì)于父類加載器是根類加載器的情況屿聋,getParent()方法返回null
2.自定義類加載器及其加載過程。
用戶自定義一個(gè)類加載器MyClassLoader.
? ? ? ?自定義類加載器loader首先從自己的命令空間中查找A類是否已經(jīng)被加載庆聘,如果已經(jīng)加載胜臊,就直接返回代表A類的Class對(duì)象引用。如果A類沒有被加載伙判,loader首先請(qǐng)求系統(tǒng)類加載器代為加載象对,系統(tǒng)類加載器再去請(qǐng)求擴(kuò)展類代為加載,擴(kuò)展類加載器再請(qǐng)求根類加載器代為加載宴抚。如根類加載器和擴(kuò)展類加載器都不能加載勒魔,則系統(tǒng)類加載器嘗試加載,如能成功菇曲,則將A類所對(duì)應(yīng)的Class對(duì)象的引用返回給loader,從而成功將A加載進(jìn)虛擬機(jī)冠绢。如系統(tǒng)類加載器不能加載A類,則loader嘗試加載常潮。若所有的父加載器及l(fā)oader本身都不能加載弟胀,則拋出ClassNotFoundException異常。
?? ? ?若有一個(gè)類加載器能成功加載A類喊式,那么這個(gè)類加載被稱為定義類加載器孵户,所有能成功返回Class對(duì)象的引用的類加載器(包括定義類加載器)都被稱為初始類加載器。在例子中系統(tǒng)類加載器稱為定義類加載器岔留,系統(tǒng)類加載器和MyClassLoader為初始類加載器夏哭。
? ? ?注意:①?需要指出的是,加載器之間的父子關(guān)系實(shí)際上指的是加載器對(duì)象之間的包裝關(guān)系献联,而不是類之間的繼承關(guān)系竖配。一對(duì)父子加載器可能是同一個(gè)加載器類的兩個(gè)實(shí)例何址,也可能不是。在子加載器對(duì)象中包裝了一個(gè)父加載器對(duì)象进胯。如上圖例子loader和loader1都是MyClassLoader類的實(shí)例用爪,并且loader1包裝了loader,并且loader為loader1的父加載器。
? ? ? ? ? ? ②每個(gè)類加載器都有自己的命名空間龄减,命名空間由該加載器及所有父加載器所加載的類組成项钮。在同一個(gè)命名空間中班眯,不會(huì)出現(xiàn)類的完整名字(包括類的包名)相同的兩個(gè)類希停;在不同的命名空間中,由可能出現(xiàn)類的完整名字(包括類的包名)相同的兩個(gè)類署隘。
? ? ? ? ? ?③由同一類加載器加載的屬于相同包的類組成了運(yùn)行時(shí)包宠能。決定兩個(gè)類是不是屬于同一個(gè)運(yùn)行時(shí)包,不僅要看它們的包名是否相同磁餐,還要看定義類加載器是否相同违崇。只有屬于同一運(yùn)行時(shí)包的類才能互相訪問包可見(即默認(rèn)訪問級(jí)別)的類和類成員。這樣的限制能避免用戶自定義的類冒充核心類庫的類诊霹,去訪問核心類庫的包可見成員羞延。假設(shè)用戶自己定義了一個(gè)類java.lang.Spy,并由用戶自定義的類加載器加載,由于java.lang.Spy和核心類庫java.lang.*由不同的加載器加載脾还,它們屬于不同的運(yùn)行時(shí)包伴箩,所以java.lang.Spy不能訪問核心類庫java.lang包中的包可見成員。
? ? ? ?④要?jiǎng)?chuàng)建用戶自己的類加載器鄙漏,只需要擴(kuò)展java.lang.ClassLoader類嗤谚,然后覆蓋它的findClass(String name)方法即可,該方法根據(jù)參數(shù)指定的類的名字怔蚌,返回對(duì)應(yīng)的Class對(duì)象引用巩步。
? ? ? ⑤同一個(gè)命名內(nèi)的類是相互可見的。自加載器的命名空間包含所有父加載器的命名空間桦踊。因此由子類加載器的類能看見父加載器加載的類椅野。例如系統(tǒng)類加載器的類能看見根類加載器加載的類。而由父加載器加載的類不能看見自加載器加載的類籍胯。如果兩個(gè)加載器之間沒有直接或間接的父子關(guān)系竟闪,那么它們各自加載的類相互不可見。
備注:1.本學(xué)習(xí)筆記是基于張龍老師JVM課程 芒炼。
? ? ? ? 2.參考資料:http://blog.csdn.net/zhoudaxia/article/details/35824249