-
熱加載:對(duì)jvm方法區(qū)中類定義進(jìn)行替換问裕,因?yàn)槎?heap)中的Class對(duì)象是對(duì)方法區(qū)對(duì)象的封裝,所以可以理解為對(duì)Class對(duì)象的替換,
當(dāng)一個(gè)class被替換后烁登,系統(tǒng)無(wú)需重啟,替換的類會(huì)立即生效蔚舀。
-
類的加載過(guò)程
- 裝載:查找并加載類的二進(jìn)制數(shù)據(jù)饵沧;
- 鏈接:
-
驗(yàn)證:確保被加載類的正確性;
- 驗(yàn)證階段是鏈接階段的第一步赌躺,目的就是確保class文件的字節(jié)流中包含的信息符合虛擬機(jī)的要求狼牺,不能危害虛擬機(jī)自身安全。驗(yàn)證階段主要包括四個(gè)檢驗(yàn)過(guò)程:文件格式驗(yàn)證礼患、元數(shù)據(jù)驗(yàn)證是钥、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。
- 文件格式驗(yàn)證
驗(yàn)證class文件格式規(guī)范 - 元數(shù)據(jù)驗(yàn)證
就是對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析讶泰,保證描述的信息符合java語(yǔ)言規(guī)范咏瑟。驗(yàn)證點(diǎn)可能包括(這個(gè)類是否有父類(除Object)、這個(gè)類是否繼承了不允許被繼承的類(final修飾的)痪署、如果這個(gè)類的父類是抽象類码泞,是否實(shí)現(xiàn)了父類或接口中要求實(shí)現(xiàn)的方法)。 - 字節(jié)碼驗(yàn)證
進(jìn)行數(shù)據(jù)流和控制流分析狼犯,這個(gè)階段對(duì)類的方法體進(jìn)行校驗(yàn)余寥,保證被校驗(yàn)的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)的行為领铐。 - 符號(hào)引用驗(yàn)證
符號(hào)引用中通過(guò)字符串描述的權(quán)限定名是否能找到對(duì)應(yīng)的類、符號(hào)引用類中的類宋舷,字段和方法的訪問(wèn)性(private protected public default)是否能被當(dāng)前類訪問(wèn)绪撵。
準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值祝蝠;
這個(gè)階段就是為類變量分配內(nèi)存并設(shè)置類變量初始值的階段音诈,這些內(nèi)存將在方法區(qū)中進(jìn)行分配。要注意的是绎狭,進(jìn)行分配內(nèi)存的只是包括類變量细溅,而不包括實(shí)例變量,實(shí)例變量是在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在java堆中的儡嘶。通常情況下喇聊,初始值為零值,假設(shè)public static int value=2;那么value在準(zhǔn)備階段過(guò)后的初始值為0蹦狂,不為2誓篱,這時(shí)候只是開(kāi)辟了內(nèi)存空間,并沒(méi)有運(yùn)行java代碼凯楔,value賦值為2的指令是程序被編譯后窜骄,存放于類構(gòu)造器<init>()方法之中,所以value被賦值為2是在初始化階段才會(huì)執(zhí)行摆屯。對(duì)于一些特殊情況啊研,如果類字段屬性表中存在ConstantValue屬性,那在準(zhǔn)備階段變量value就會(huì)被初始化為ContstantValue屬性所指的值鸥拧,那么對(duì)于上面value,編譯時(shí)javac將會(huì)為value生成ConstantValue屬性,在準(zhǔn)備階段虛擬機(jī)就會(huì)根據(jù)ConstantValue將value設(shè)置為2-
解析:解析階段是虛擬機(jī)常量池的符號(hào)引用替換為直接引用的過(guò)程削解。
- 符號(hào)引用:符號(hào)引用是一組符號(hào)來(lái)描述所引用的對(duì)象富弦,符號(hào)可以是任何形式的字面量,只要使用時(shí)能定位到目標(biāo)即可氛驮,符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無(wú)關(guān)腕柜,引用的目標(biāo)對(duì)象并不一定已經(jīng)加載到內(nèi)存中。
- 直接引用:直接引用可以是直接指向目標(biāo)對(duì)象的指針矫废、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄(一種特殊的智能指針)盏缤。直接引用是與虛擬機(jī)內(nèi)存布局實(shí)現(xiàn)相關(guān),同一個(gè)符號(hào)引用在不同虛擬機(jī)實(shí)例上翻譯出來(lái)的直接引用一般不會(huì)相同蓖扑,如果有了直接引用唉铜,那引用的目標(biāo)必定存在內(nèi)存中。
- 解析過(guò)程主要針對(duì)類或接口律杠、字段潭流、類方法竞惋、接口方法四類符號(hào)引用進(jìn)行。
-
- 初始化:為類的靜態(tài)變量賦予正確的初始值灰嫉;
- 類的初始化階段是加載過(guò)程的最后一步拆宛,在準(zhǔn)備階段,類變量已賦過(guò)一次系統(tǒng)要求的初始值讼撒,而在初始化階段浑厚,則是根據(jù)程序員通過(guò)程序制定的主觀計(jì)劃去初始化類變量和其他資源,或者可以從另外一個(gè)角度表達(dá):初始化階段是執(zhí)行類構(gòu)造器<init>()方法的過(guò)程根盒。在以下四種情況下初始化過(guò)程會(huì)被觸發(fā)執(zhí)行:
- 遇到new钳幅、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時(shí)郑象,如果類沒(méi)有進(jìn)行過(guò)初始化贡这,則需先觸發(fā)其初始化。生成這4條指令的最常見(jiàn)的java代碼場(chǎng)景是:使用new關(guān)鍵字實(shí)例化對(duì)象厂榛、讀取或設(shè)置一個(gè)類的靜態(tài)字段(被final修飾盖矫、已在編譯器把結(jié)果放入常量池的靜態(tài)字段除外)的時(shí)候,以及調(diào)用類的靜態(tài)方法的時(shí)候击奶。
- 使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候辈双。
- 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒(méi)有進(jìn)行過(guò)初始化柜砾、則需要先出發(fā)其父類的初始化湃望。
- jvm啟動(dòng)時(shí),用戶指定一個(gè)執(zhí)行的主類(包含main方法的那個(gè)類)痰驱,虛擬機(jī)會(huì)先初始化這個(gè)類证芭。
- 類構(gòu)造器<init>()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static塊)中的語(yǔ)句合并產(chǎn)生的,編譯器收集的順序是由語(yǔ)句在源文件中出現(xiàn)的順序所決定的担映,靜態(tài)語(yǔ)句塊中只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量废士,定義在它之后的變量,在前面的靜態(tài)語(yǔ)句快可以賦值蝇完,但是不能訪問(wèn)官硝。
- 類構(gòu)造器<init>()方法與類的構(gòu)造函數(shù)(實(shí)例構(gòu)造函數(shù)<init>()方法)不同,它不需要顯式調(diào)用父類構(gòu)造短蜕,虛擬機(jī)會(huì)保證在子類<init>()方法執(zhí)行之前氢架,父類的<init>()方法已經(jīng)執(zhí)行完畢。因此在虛擬機(jī)中的第一個(gè)執(zhí)行的<init>()方法的類肯定是java.lang.Object朋魔。
- 由于父類的<init>()方法先執(zhí)行岖研,也就意味著父類中定義的靜態(tài)語(yǔ)句快要優(yōu)先于子類的變量賦值操作。
- <init>()方法對(duì)于類或接口來(lái)說(shuō)并不是必須的警检,如果一個(gè)類中沒(méi)有靜態(tài)語(yǔ)句缎玫,也沒(méi)有變量賦值的操作硬纤,那么編譯器可以不為這個(gè)類生成<init>()方法。
- 接口中不能使用靜態(tài)語(yǔ)句塊赃磨,但接口與類不太能夠的是筝家,執(zhí)行接口的<init>()方法不需要先執(zhí)行父接口的<init>()方法。只有當(dāng)父接口中定義的變量被使用時(shí)邻辉,父接口才會(huì)被初始化溪王。另外,接口的實(shí)現(xiàn)類在初始化時(shí)也一樣不會(huì)執(zhí)行接口的<init>()方法值骇。
- 虛擬機(jī)會(huì)保證一個(gè)類的<init>()方法在多線程環(huán)境中被正確加鎖和同步莹菱,如果多個(gè)線程同時(shí)去初始化一個(gè)類,那么只會(huì)有一個(gè)線程執(zhí)行這個(gè)類的<init>()方法吱瘩,其他線程都需要阻塞等待道伟,直到活動(dòng)線程執(zhí)行<init>()方法完畢。如果一個(gè)類的<init>()方法中有耗時(shí)很長(zhǎng)的操作使碾,那就可能造成多個(gè)進(jìn)程阻塞蜜徽。
- 準(zhǔn)備階段和初始化階段看似有點(diǎn)牟盾,其實(shí)是不牟盾的票摇,
如果類中有語(yǔ)句:private static int a = 10拘鞋,
它的執(zhí)行過(guò)程是這樣的:首先字節(jié)碼文件被加載到內(nèi)存后,
先進(jìn)行鏈接的驗(yàn)證這一步驟矢门,驗(yàn)證通過(guò)后準(zhǔn)備階段盆色,
給a分配內(nèi)存,因?yàn)樽兞縜是static的祟剔,
所以此時(shí)a等于int類型的默認(rèn)初始值0隔躲,
即a=0,然后到解析,到初始化這一步驟時(shí)物延,
才把a(bǔ)的真正的值10賦給a,此時(shí)a=10蹭越。 - 加載(裝載)、驗(yàn)證教届、準(zhǔn)備、初始化和卸載這五個(gè)階段順序是固定的驾霜,類加載過(guò)程必須按照這種順序開(kāi)始案训,而解析階段不一定,它在某些情況下可以在初始化之后再開(kāi)始粪糙, 這是為了運(yùn)行時(shí)動(dòng)態(tài)綁定特性(JIT例如接口只在調(diào)用的時(shí)候才知道具體的實(shí)現(xiàn)的是哪個(gè)子類)强霎。值得注意的是:這些階段通常都是交叉的混合式進(jìn)行的,通常會(huì)在一個(gè)階段執(zhí)行的過(guò)程中調(diào)用或激活另一個(gè)階段蓉冈。
類加載器
JVM設(shè)計(jì)者把類加載階段中的通過(guò)類全名來(lái)獲取此類的二進(jìn)制字節(jié)流這個(gè)動(dòng)作放到j(luò)ava虛擬機(jī)外部去實(shí)現(xiàn)城舞,以便讓應(yīng)用程序決定如何獲取所需要的類轩触。實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊成為“類加載器”。類與類加載器
對(duì)于任何一個(gè)類家夺,都需要由加載它的類加載器和這個(gè)類來(lái)確定其在JVM中的唯一性脱柱。也就是說(shuō),兩個(gè)類來(lái)源于同一個(gè)Class文件拉馋,并且被同一個(gè)類加載器加載榨为,這兩個(gè)類才相等。-
雙親委派模型
- 從虛擬機(jī)的角度來(lái)說(shuō)煌茴,有兩種不同的類加載器:一種是啟動(dòng)類加載器(Bootstrap ClassLoader),該加載器使用C++語(yǔ)言實(shí)現(xiàn)随闺,屬于虛擬機(jī)自身的一部分。另一部分就是所有其它的類加載器蔓腐,這些類加載器是由Java語(yǔ)言實(shí)現(xiàn)矩乐,獨(dú)立于JVM外部,并且全部繼承抽象類java.lang.ClassLoader.
- 從java開(kāi)發(fā)人員的角度看回论,大部分java程序會(huì)用到以下三種系統(tǒng)提供的類加載器:
啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載JAVA_HOME\lib目錄中并且能被虛擬機(jī)識(shí)別的類庫(kù)加載到JVM內(nèi)存中散罕,如果名稱不符合的類庫(kù)即使在lib目錄中也不會(huì)被加載。該類加載器無(wú)法被java程序直接引用透葛。
擴(kuò)展類加載器(Extension ClassLoader):該加載器主要負(fù)責(zé)加載JAVA_HOME\lib\ext目錄中的類庫(kù)笨使,開(kāi)發(fā)者可以使用擴(kuò)展加載器。
應(yīng)用程序類加載器(Application ClassLoader):該列加載器也稱為系統(tǒng)加載器僚害,它負(fù)責(zé)加載用戶類路徑(Classpath)上所指定的類庫(kù)硫椰,開(kāi)發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒(méi)有自定義過(guò)自己的類加載器萨蚕,一般情況下這個(gè)就是程序中默認(rèn)的類加載器靶草。
當(dāng)然除了以上三種類加載器,我們還能自己定義類加載器岳遥。這些類加載器之間的關(guān)系如下奕翔。
上面的這種模型,就稱為類加載器的雙親委派模型浩蓉。該模型要求除了頂層的啟動(dòng)類加載器外派继,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。子類加載器不是以繼承的關(guān)系來(lái)實(shí)現(xiàn)捻艳,而是通過(guò)組合關(guān)系來(lái)復(fù)用父加載器的代碼驾窟。
雙親委派模型的工作過(guò)程為:如果一個(gè)類加載器收到了類請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類认轨,而是把這個(gè)請(qǐng)求委派給父加載器去完成绅络,每一層都是如此,因此所有類加載的請(qǐng)求都會(huì)傳到啟動(dòng)類加載器,只有當(dāng)父加載器無(wú)法完成該請(qǐng)求時(shí)恩急,子加載器才去自己加載杉畜。
雙親委派模型的好處就是java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。例如:Object,無(wú)論那個(gè)類加載器去加載該類衷恭,最終都是由啟動(dòng)類加載器進(jìn)行加載的此叠,因此Object類在程序的各種類加載環(huán)境中都是一個(gè)類。如果不用改模型匾荆,那么java.lang.Object類存放在classpath中拌蜘,那么系統(tǒng)中就會(huì)出現(xiàn)多個(gè)Object類,程序變得很混亂牙丽。-
一般來(lái)說(shuō)简卧,這四種類加載器會(huì)形成一種父子關(guān)系,高層為低層的父加載器烤芦。在類進(jìn)行加載時(shí)举娩,
首先會(huì)自底向上挨個(gè)檢查是否已經(jīng)加載了指定類,如果已經(jīng)加載构罗,則直接返回該類的引用铜涉。
如果到最高層也沒(méi)有找到加載過(guò)指定類,那么會(huì)自頂向下挨個(gè)嘗試加載遂唧,直到用戶自定義類加載器芙代,
如果還不能成功,就會(huì)拋出異常盖彭。
類加載java.lang.ClassLoader類中纹烹,有一個(gè)loadClass方法源碼如下:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先檢查該name指定的class是否有被加載 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { // 如果parent不為null,則調(diào)用parent的loadClass進(jìn)行加載 c = parent.loadClass(name, false); } else { // parent為null召边,則調(diào)用BootstrapClassLoader進(jìn)行加載 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 如果仍然無(wú)法加載成功铺呵,則調(diào)用自身的findClass進(jìn)行加載 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
- 從源代碼中,我們不難看出隧熙,先檢查是否已經(jīng)加載片挂,如果沒(méi)有,就調(diào)用父加載器的loadClass()方法贞盯,如果父加載器為空則默認(rèn)使用啟動(dòng)類加載器作為父加載器音念。如果父加載器加載失敗,拋出ClassNotFountException躏敢,然后再調(diào)用findClass()方法加載闷愤。
-
自定義類加載器
若要實(shí)現(xiàn)自定義類加載器,只需要繼承java.lang.ClassLoader類父丰,并且重寫findClass()方法即可。 java.lang.ClassLoader類的基本職責(zé)就是根據(jù)一個(gè)指定的類的名稱,找到或者生成對(duì)應(yīng)的字節(jié)碼蛾扇,然后從這些字節(jié)碼中定義出一個(gè)Java類攘烛,即java.lang.Class類的一個(gè)實(shí)例。除此之外镀首,ClassLoader還負(fù)責(zé)加載Java應(yīng)用所需的資源坟漱,如圖像文件和配置文件等,熱替換也是基于該類更哄,來(lái)繞過(guò)Java類的既定加載過(guò)程
ClassLoader中與加載類相關(guān)的方法如下:方法 說(shuō)明 findLoadedClass():該方法會(huì)在對(duì)應(yīng)加載器的名字空間中尋找指定的類是否已存在芋齿,如果存在就返回給類的引用, 否則就返回null成翩。每個(gè)類加載器都維護(hù)有自己的一份已加載類名字空間觅捆,其中不能出現(xiàn)兩個(gè)同名的類。 凡是通過(guò)該類加載器加載的類麻敌,無(wú)論是直接的還是間接的栅炒,都保存在自己的名字空間中,這里的直接是指术羔, 存在于該類加載器的加載路徑上并由該加載器完成加載赢赊,間接是指,由該類加載器把類的加載工作委托給其他類加載器完成類的實(shí)際加載级历。 getSystemClassLoader():該方法返回系統(tǒng)使用的ClassLoader释移。可以在 自定義的類加載器中通過(guò)該方法把一部分工作轉(zhuǎn)交給系統(tǒng)類加載器去處理寥殖。 defineClass():該方法接收以字節(jié)數(shù)組表示的類字節(jié)碼玩讳,并把它轉(zhuǎn)換成Class實(shí)例。 系統(tǒng)自帶的ClassLoader扛禽,默認(rèn)加載程序的是AppClassLoader锋边, ClassLoader加載一個(gè)class,最終調(diào)用的是defineClass(…)方法编曼, 這時(shí)候就在想是否可以重復(fù)調(diào)用defineClass(…)方法加載同一個(gè)類(或者修改過(guò))豆巨, 最后發(fā)現(xiàn)調(diào)用多次的話會(huì)有相關(guān)錯(cuò)誤: java.lang.LinkageError attempted duplicate class definition 所以一個(gè)class被一個(gè)ClassLoader實(shí)例加載過(guò)的話, 就不能再被這個(gè)ClassLoader實(shí)例再次加載(這里的加載指的是掐场,調(diào)用了defileClass(...)方法往扔, 重新加載字節(jié)碼、解析熊户、驗(yàn)證)萍膛。而系統(tǒng)默認(rèn)的AppClassLoader加載器, 他們內(nèi)部會(huì)緩存加載過(guò)的class嚷堡,重新加載的話蝗罗,就直接取緩存艇棕。 所以對(duì)于熱加載的話,只能重新創(chuàng)建一個(gè)ClassLoader串塑,然后再去加載已經(jīng)被加載過(guò)的class文件沼琉。 loadClass():加載類的入口方法,調(diào)用該方法完成類的顯式加載桩匪。通過(guò)對(duì)該方法的重寫打瘪, 可以完全控制和管理類的加載過(guò)程。執(zhí)行l(wèi)oadClass方法傻昙,只是單純的把類加載到內(nèi)存闺骚, 并不是對(duì)類的主動(dòng)使用,不會(huì)引起類的初始化妆档。 該方法轉(zhuǎn)換一個(gè)類的同時(shí)僻爽,會(huì)先要求裝載該類的父類以及實(shí)現(xiàn)的接口類。是ClassLoader的入口點(diǎn)过吻。 當(dāng)一個(gè)類沒(méi)有指明用什么加載器加載的時(shí)候进泼,JVM默認(rèn)采用AppClassLoader加載器 加載沒(méi)有加載過(guò)的class,調(diào)用的方法的入口就是loadClass(…)纤虽。 如果一個(gè)class被自定義的ClassLoader加載乳绕, 那么JVM也會(huì)調(diào)用這個(gè)自定義的ClassLoader.loadClass(…) 方法來(lái)加載class內(nèi)部引用的一些別的class文件。重載這個(gè)方法逼纸, 能實(shí)現(xiàn)自定義加載class的方式洋措,會(huì)拋棄雙親委托機(jī)制,但是即使不采 用雙親委托機(jī)制杰刽, 比如java.lang包中的相關(guān)類還是不能自定義一個(gè)同名的類來(lái)代替菠发,主要因?yàn)镴VM解析、 驗(yàn)證class的時(shí)候贺嫂,會(huì)進(jìn)行相關(guān)判斷滓鸠。 resolveClass():鏈接一個(gè)指定的類。這是一個(gè)在某些情況下確保類可用的必要方法第喳。
- 總結(jié)
- 要想實(shí)現(xiàn)同一個(gè)類的不同版本的共存糜俗,那么這些不同版本必須由不同的類加載器進(jìn)行加載,因此就不能把這些類的加載工作委托給系統(tǒng)加載器來(lái)完成曲饱,因?yàn)樗鼈冎挥幸环荨?/li>
- 為了做到這一點(diǎn)悠抹,就不能采用系統(tǒng)默認(rèn)的類加載器委托規(guī)則,也就是說(shuō)我們定制的類加載器的父加載器必須設(shè)置為null扩淀。
- 總結(jié)
-
補(bǔ)充資料:關(guān)于類加載器的命名空間
- 命名空間由加載器和所有的父加載器所加載的類構(gòu)成楔敌;
- 在同一個(gè)命名空間中,不可能出現(xiàn)類名相同的兩個(gè)類驻谆;
- 在不同的命名空間中卵凑,可能出現(xiàn)類名相同的兩個(gè)類(類名指類全稱)庆聘;
- 由子加載器加載的類能看見(jiàn)父加載器加載的類,反之不可以勺卢;(比如java.lang.String類掏觉,我們自己寫的類肯定能看見(jiàn),但是父加載器肯定看不見(jiàn)我們自己定義的類)
- 如果兩個(gè)加載器之間沒(méi)有直接或者間接的父子關(guān)系值漫,那么兩個(gè)加載器加載的類是相互不可見(jiàn)的;
-
自定義ClassLoader
- CustomClassLoader
package com.coding.classloader; import org.apache.log4j.Logger; import java.io.*; import java.lang.reflect.Method; import java.net.URL; import java.util.HashSet; import java.util.Timer; import java.util.TimerTask; public class CustomClassLoader extends ClassLoader { private Logger logger = Logger.getLogger(CustomClassLoader.class); private String basedir; // 需要該類加載器直接加載的類文件的基目錄 private HashSet classNames; // 需要由該類加載器直接加載的類名 public CustomClassLoader(String basedir, String[] clazzNames) throws Exception { super(null); // 指定父類加載器為 null this.basedir = basedir; classNames = new HashSet(); for (int i = 0; i < clazzNames.length; i++) { String name = clazzNames[i]; StringBuffer sb = new StringBuffer(basedir); String className = name.contains(".") ? name.replace('.', File.separatorChar)+ ".class" : name+ ".class"; sb.append(className); logger.debug("Load Class: baseDir:" + basedir + ",ClassName:" + name + ",Class file path:" + sb.toString()); File classF = new File(sb.toString()); FileInputStream fin = new FileInputStream(classF); byte[] raw = new byte[(int) classF.length()]; fin.read(raw); fin.close(); defineClass(name, raw, 0, raw.length); classNames.add(clazzNames[i]); } } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class cls = null; cls = findLoadedClass(name); if (!this.classNames.contains(name) && cls == null) cls = getSystemClassLoader().loadClass(name); if (cls == null) throw new ClassNotFoundException(name); if (resolve) resolveClass(cls); return cls; } public static void main(String[] args){ new Timer().schedule(new TimerTask() { @Override public void run() { try { URL resource = CustomClassLoader.class.getResource("/"); String resourceFile = resource.getFile(); System.out.println(resourceFile); // 每次都創(chuàng)建出一個(gè)新的類加載器 CustomClassLoader customClassLoader = new CustomClassLoader( resourceFile, new String[] { "com.coding.classloader.Foo" }); Class<?> cls = customClassLoader.loadClass("com.coding.classloader.Foo"); // ClassLoader classLoader1 = cls.getClassLoader();//com.coding.classloader.CustomClassLoader // Class<?> cls2 = Class.forName("com.coding.classloader.Foo"); // ClassLoader classLoader = cls2.getClassLoader();//sun.misc.Launcher$AppClassLoader // System.out.println(classLoader+":"+classLoader1); Object foo = cls.newInstance(); Method m = foo.getClass().getMethod("sayHi", new Class[] {}); m.invoke(foo, new Object[] {}); } catch (Exception ex) { ex.printStackTrace(); } } }, 0, 1000L); } }
- 嘗試熱加載的類
package com.coding.classloader; public class Foo { public void sayHi() { System.out.println("hi\t v1"); } }
- 測(cè)試
- 運(yùn)行main方法,此時(shí)
sayHi
方法打印出hi v1
- 修改
sayHi
方法织盼,使之打印出hi v2
杨何,重新編譯, - 生成新class沥邻,覆蓋舊的class危虱,立馬生效,打印
h1 v2
- 運(yùn)行main方法,此時(shí)
Java ClassLoader 和熱加載
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門方庭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)厕吉,“玉大人,你說(shuō)我怎么就攤上這事械念⊥分欤” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵龄减,是天一觀的道長(zhǎng)项钮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)欺殿,這世上最難降的妖魔是什么寄纵? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮脖苏,結(jié)果婚禮上程拭,老公的妹妹穿的比我還像新娘。我一直安慰自己棍潘,他們只是感情好恃鞋,可當(dāng)我...
- 文/花漫 我一把揭開(kāi)白布崖媚。 她就那樣靜靜地躺著,像睡著了一般恤浪。 火紅的嫁衣襯著肌膚如雪畅哑。 梳的紋絲不亂的頭發(fā)上,一...
- 那天水由,我揣著相機(jī)與錄音荠呐,去河邊找鬼。 笑死砂客,一個(gè)胖子當(dāng)著我的面吹牛泥张,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞠值,決...
- 文/蒼蘭香墨 我猛地睜開(kāi)眼媚创,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了彤恶?” 一聲冷哼從身側(cè)響起钞钙,我...
- 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎声离,沒(méi)想到半個(gè)月后芒炼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡术徊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年焕议,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弧关。...
- 正文 年R本政府宣布株憾,位于F島的核電站蝙寨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嗤瞎。R本人自食惡果不足惜墙歪,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贝奇。 院中可真熱鬧虹菲,春花似錦、人聲如沸掉瞳。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至霎褐,卻和暖如春址愿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冻璃。 一陣腳步聲響...
- 正文 我出身青樓歌粥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拍埠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 1 基本信息 每個(gè)開(kāi)發(fā)人員對(duì)java.lang.ClassNotFoundExcetpion這個(gè)異惩辆樱肯定都不陌生枣购,...
- 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存, 并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)擦耀、轉(zhuǎn)換解析和初始化棉圈, 最終形成可以被虛擬機(jī)直接使...
- 作者:成 富, 軟件工程師, IBM 中國(guó)軟件開(kāi)發(fā)中心 類加載器(class loader)是 Java?中的一個(gè)...
- 原文鏈接:http://iaspecwang.iteye.com/blog/1931043 一.概述 定義:虛擬機(jī)...
- 九月秋吁系,華山早已層林盡染德召,枯枝黃葉掩蓋了整座大山,華南院座落于華山腳下汽纤,在院內(nèi)即可仰觀華山秋之美景上岗,如此秋高氣爽之...