? ? java類加載器是將java class字節(jié)碼加載到內(nèi)存中的組件胚嘲,分為引導(dǎo)類加載器(bootstrapClassLoader)、擴(kuò)展加載器(ExtensionClassLoader)否淤、應(yīng)用類加載器(appClassLoader)下面分別介紹這三種類加載器。
? ? 1.引導(dǎo)類加載器(bootStrapClassLoader)
? ?引導(dǎo)類加載器是有c++編寫,主要作用為加載java環(huán)境本身需要的class,主要包含java_home/lib 目錄下java核心類庫(kù)疙剑,以及-xbootclasspath參數(shù)指定的類庫(kù),值得注意的是該類加載器只加載jdk本身的類庫(kù)稽煤,即使在lib目錄下放入新的jar包核芽,該類加載也不會(huì)加載。該類加載器要求加載的類包頭還必須是sun酵熙、java轧简、javax。
? ? 2.擴(kuò)展類加載器(extensionClassLoader)
? ? 開發(fā)者可以直接使用擴(kuò)展類加載器匾二,該加載器負(fù)責(zé)加載java_home/lib/ext目錄下類庫(kù)哮独。開發(fā)者也可以使用擴(kuò)展類加載器加載-Djava.ext.dir指定目錄下的類庫(kù)拳芙。
? ? 3.應(yīng)用類加載器(appClassLoader)
? ? 應(yīng)用類加載器主要用于加載系統(tǒng)應(yīng)用的class文件。它負(fù)責(zé)加載系統(tǒng)類路徑j(luò)ava -classpath或-D java.class.path?指定路徑下的類庫(kù)皮璧,也就是我們經(jīng)常用到的classpath路徑舟扎,開發(fā)者可以直接使用系統(tǒng)類加載器,一般情況下該類加載是程序中默認(rèn)的類加載器悴务,通過getSystemClassLoader()方法可以獲取到該類加載器睹限。?
Java虛擬機(jī)對(duì)class文件采用的是按需加載的方式,也就是說當(dāng)需要使用該類時(shí)才會(huì)將它的class文件加載到內(nèi)存生成class對(duì)象讯檐,而且加載某個(gè)類的class文件時(shí)羡疗,Java虛擬機(jī)采用的是雙親委派模式即把加載類的動(dòng)作交由父類處理,是一種任務(wù)委派模式别洪。
????雙親委派模式
? ? 在java中類加載器被要求都要有父加載器叨恨,除了頂層的引導(dǎo)類加載器。但是雙親委派模式中的父子關(guān)系并非所說的類繼承關(guān)系中的父子挖垛,而是采用一種組合的關(guān)系復(fù)用父加載器的代碼痒钝。
但是當(dāng)你看java jdk這幾個(gè)類源碼是通常會(huì)有困惑,因?yàn)樗麄兊睦^承關(guān)系并非如圖1所示痢毒,而是如圖2所示送矩,這是因?yàn)樯厦嫠f的雙親委托中的雙親和兒子并不是指類繼承關(guān)系中的父類,而是以調(diào)用優(yōu)先順序來表示的父子關(guān)系闸准。如appClassLoader中的parent屬性指向了extClassLoader益愈,我們稱extClassLoader在雙親委托模式中是appClassLoader的父加載器。假設(shè)我們自定義一個(gè)MyClassLoader繼承ClassLoader類夷家,我們發(fā)現(xiàn)MyClassLoader中的parent依然指向的是appClassLoader蒸其。也就是說在類關(guān)系圖中,MyClassLoader是ClassLoader的子類库快,但是在雙親委托模式鏈中MyClassLoader是AppClassLoader的子節(jié)點(diǎn)摸袁。這是為什么呢?我們來看下ClassLoader的構(gòu)造函數(shù)
如圖ClassLoader類在沒有顯試指定父節(jié)點(diǎn)(parent)是誰的時(shí)候义屏,默認(rèn)是Launcher.getClassLoader()獲取靠汁,即AppClassLoader,至此真相大白闽铐!
tip
tip1蝶怔、類加載器與main方法 與jjava進(jìn)程之間的關(guān)系。
當(dāng)我們啟動(dòng)一個(gè)Java程序兄墅,即啟動(dòng)一個(gè)main方法時(shí)踢星,都將啟動(dòng)一個(gè)Java虛擬機(jī)進(jìn)程,不管這個(gè)進(jìn)程有多么復(fù)雜隙咸。而不同的JVM進(jìn)程之間是不會(huì)相互影響的沐悦。這也就是為什么說成洗,Java程序只有一個(gè)入口——main方法,讓虛擬機(jī)調(diào)用藏否。而兩個(gè)mian方法瓶殃,對(duì)應(yīng)的是2個(gè)JVM進(jìn)程,啟動(dòng)的是兩個(gè)不同的類加載器副签,操作的實(shí)際上是不同的類遥椿。故而不會(huì)互相影響。
tip2继薛、之前看完java類加載器的關(guān)系修壕,一直心里有疑惑愈捅,覺得哪里有什么問題遏考,又不知道問題在哪,看完tomcat的類加載機(jī)制蓝谨,在寫這些文章的時(shí)候灌具,想明白了問題和答案。我發(fā)現(xiàn)在寫東西的時(shí)候能更好的梳理邏輯譬巫。問題如下:如果java按照雙親委派模式加載類咖楣,那豈不是所有的類都會(huì)被最頂層的類加載器所加載?bootStrap顯然不會(huì)只會(huì)加載核心類包 (rt.jar),ExtClassLoader也是會(huì)加載ext目錄下jar包芦昔,那么AppClassLoader呢诱贿?我猜AppClassLoader會(huì)加載main函數(shù)所在classpath。另外所有的自定義classloader都會(huì)加載指定目錄下class咕缎,這樣就不會(huì)出現(xiàn)所有class都由AppClassLoader加載的情況了珠十。證據(jù)如下URLclassLoader的構(gòu)造函數(shù)如下:
說明classLoader需要指定類加載路徑。
tip3凭豪、一個(gè)應(yīng)用里類靜態(tài)變量永遠(yuǎn)只有一個(gè)值嗎焙蹭?
我們都知道靜態(tài)變量屬于類,所有的對(duì)象共享一個(gè)值嫂伞。在了解java類加載器之前孔厉,一直認(rèn)為一個(gè)靜態(tài)變量在一個(gè)應(yīng)用里只有一個(gè)值。但是學(xué)習(xí)完java類加載后就想到可能不是帖努,如果這個(gè)類被兩個(gè)類加載器加載撰豺,那么它的靜態(tài)變量就可能有兩個(gè)值。我們寫個(gè)程序來驗(yàn)證下拼余,如下:
可以看到污桦,當(dāng)兩個(gè)類加載器分別加載了class后,class對(duì)象不是同一個(gè)姿搜。通過反射把c1的say值改為hello2寡润,c2的say值還是hello捆憎。驗(yàn)證完畢。