日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開源 > php教程 > 【深入理解Java虛擬機(jī)】類加載機(jī)制

【深入理解Java虛擬機(jī)】類加載機(jī)制

來(lái)源:程序員人生   發(fā)布時(shí)間:2016-07-01 15:24:10 閱讀次數(shù):2427次

本文內(nèi)容來(lái)源于《深入理解Java虛擬機(jī)》1書,非常推薦大家去看1下這本書。
本系列其他文章:
【深入理解Java虛擬機(jī)】Java內(nèi)存區(qū)域模型、對(duì)象創(chuàng)建進(jìn)程、常見OOM
【深入理解Java虛擬機(jī)】垃圾回收機(jī)制


1、類加載機(jī)制概述

虛擬機(jī)把描寫類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,終究構(gòu)成可以被虛擬機(jī)直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制
在java中,類型的加載、連接和初始化進(jìn)程都是在程序運(yùn)行期間完成的,這類策略雖然會(huì)帶來(lái)1些性能開消,但是卻為java利用程序提供了高度的靈活性,java動(dòng)態(tài)擴(kuò)大的語(yǔ)言特性就是依賴運(yùn)行期動(dòng)態(tài)加載和動(dòng)態(tài)鏈接這個(gè)特點(diǎn)構(gòu)成的,所謂java動(dòng)態(tài)擴(kuò)大,比如,如果編寫了1個(gè)面向接口的利用程序,可以等到運(yùn)行時(shí)再指定其實(shí)際的實(shí)現(xiàn)類。

2、類加載的時(shí)機(jī)

類從被加載到虛擬機(jī)內(nèi)存中開始,到卸載出內(nèi)存為止,全部生命周期包括:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用、卸載,共7個(gè)階段。其中,驗(yàn)證、準(zhǔn)備、解析3個(gè)階段稱為連接(Linking),7個(gè)進(jìn)程產(chǎn)生順序以下:

上面這7個(gè)進(jìn)程,除解析這個(gè)進(jìn)程外,其余進(jìn)程必須循序漸進(jìn)地履行,即順序是肯定的,而解析進(jìn)程不1定,在某些情況下可以在初始化階段以后再履行,這是為了支持java語(yǔ)言的運(yùn)行時(shí)綁定(也稱為動(dòng)態(tài)綁定或晚期綁定)。

java虛擬機(jī)規(guī)范中,并沒(méi)有規(guī)定類加載進(jìn)程中的第1個(gè)階段(即加載階段)的履行時(shí)機(jī),但是對(duì)初始化階段,虛擬機(jī)規(guī)范中嚴(yán)格規(guī)定了“有且只有”下面5種情況下必須立即對(duì)類進(jìn)行初始化(而這時(shí)候,加載、驗(yàn)證、準(zhǔn)備自然需要在此之前開始):
(1)遇到new、getstatic、putstatic、invokestatic這4條指令時(shí),必須觸發(fā)其初始化。這4條指令最多見的場(chǎng)景是:使用new關(guān)鍵字實(shí)例化對(duì)象、讀取或設(shè)置1個(gè)類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外,即常量除外)、調(diào)用1個(gè)類的靜態(tài)方法的時(shí)候;
(2)進(jìn)行反射調(diào)用的時(shí)候;
(3)初始化1個(gè)類的時(shí)候,如果其父類還沒(méi)有初始化,則需要先觸發(fā)其父類的初始化;
(4)當(dāng)虛擬機(jī)啟動(dòng)時(shí),需要先初始化那個(gè)包括main方法的要履行的主類;
(5)當(dāng)使用JDK1.7的動(dòng)態(tài)語(yǔ)言支持時(shí),如果1個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果為REF_getStatic 、REF_putStatic、REF_invokeStatic的方法句柄,句柄對(duì)應(yīng)的類會(huì)被初始化;

上面5種場(chǎng)景觸發(fā)類進(jìn)行初始化的行動(dòng)稱為對(duì)1個(gè)類進(jìn)行“主動(dòng)援用”,除此以外,所有其他援用類的方式都不會(huì)觸發(fā)初始化步驟(注意,此時(shí)已是援用了,只不過(guò)不會(huì)觸發(fā)初始化,其他階段是不是觸發(fā)要看具體虛擬機(jī)的實(shí)現(xiàn)),這些援用稱為“被動(dòng)援用”。
被動(dòng)援用的幾個(gè)例子
(1)對(duì)靜態(tài)字段,只有直接定義這個(gè)字段的類才會(huì)被初始化,因此通過(guò)其子類來(lái)援用父類中定義的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化而不會(huì)觸發(fā)子類的初始化。至因而否要?jiǎng)由碜宇惖募虞d、驗(yàn)證需要看具體虛擬機(jī)實(shí)現(xiàn);以下:
class SuperClass{ static{ System.out.println("SuperClass init!"); } public static int value = 123; } class SubClass extends SuperClass{ static{ System.out.println("SubClass init!");//子類中援用父類的靜態(tài)字段,不會(huì)致使類初始化 } } public class Test { public static void main(String[] args) { System.out.println(SubClass.value); } }
運(yùn)行結(jié)果:
SuperClass init!
123
可以看到,只會(huì)打印出父類的初始化語(yǔ)句。

(2)通過(guò)數(shù)組定義來(lái)援用類,不會(huì)觸發(fā)此類的初始化。如 A[] ints = new A[10] ,  不會(huì)觸發(fā)A 類的初始化。而是會(huì)觸發(fā)名為 LA的類初始化。它是1個(gè)由虛擬機(jī)自動(dòng)生成的、直接繼承于Object 的子類,創(chuàng)建動(dòng)作由字節(jié)碼指令 newarray 觸發(fā)。這個(gè)類代表了1個(gè)元素類型為 A 的1位數(shù)組,數(shù)組中的屬性和方法都實(shí)現(xiàn)在這個(gè)類中。Java 語(yǔ)言中數(shù)組的訪問(wèn)比C/C++ 安全是由于這個(gè)類封裝了數(shù)組元素的訪問(wèn)方法。以下:
public class Test {
    public static void main(String[] args) {
        SuperClass[] sca = new SuperClass[10];
    }
}
SuperClass類為上面的那個(gè),運(yùn)行后發(fā)現(xiàn)并沒(méi)有打印出SuperClass init!,說(shuō)明沒(méi)有觸發(fā)SuperClass類的初始化階段。

(3)常量在編譯階段會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒(méi)有直接援用到定義常量的類,因此不會(huì)觸發(fā)定義常量的類的初始化,以下:
class ConstClass{ static{ System.out.println("ConstClass init!"); } public static final String HELLOWORLD = "hello world"; } public class Test { public static void main(String[] args) { System.out.println(ConstClass.HELLOWORLD); } }
運(yùn)行結(jié)果:
hello world
只是輸出了hello world,并沒(méi)有輸出ConstClass init!,可見ConstClass類并沒(méi)有被初始化。

注意:
上面講的3個(gè)例子是被動(dòng)援用的情況,很多情況下我們會(huì)通過(guò)new來(lái)初始化1個(gè)類,這個(gè)情形它屬于上面提到的5種主動(dòng)援用的場(chǎng)景,因此會(huì)觸發(fā)這個(gè)類的初始化,如果這個(gè)類有父類的話,會(huì)先觸發(fā)父類的初始化。注意不要和上面的被動(dòng)援用弄混了。

接口的初始化
上面代碼中用static語(yǔ)句塊進(jìn)行初始化,而結(jié)構(gòu)中不能使用static語(yǔ)句塊,但是編譯器依然回味接口生成<clinit>()類構(gòu)造器來(lái)初始化接口中的成員變量(常量);接口與類初始化的區(qū)分主要是在上面5種主動(dòng)援用中的第3種:當(dāng)1個(gè)類在初始化時(shí),要求其父類全部已初始化過(guò)了,但是對(duì)接口的初始化來(lái)講,并不要求父接口全部都完成了初始化,只有在真正使用到付接口的時(shí)候(如援用接口中定義的常量)才會(huì)初始化

3、類加載進(jìn)程

3.1 加載

在加載階段,需要完成3件事情:

(1)通過(guò)1個(gè)類的全限定名來(lái)獲得其定義的2進(jìn)制字節(jié)流。

(2)將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。

(3)在內(nèi)存中生成1個(gè)代表這個(gè)類的java.lang.Class對(duì)象(并沒(méi)有明確規(guī)定是在java堆中,對(duì)HotSpot虛擬機(jī)來(lái)講,Class對(duì)象比較特殊,它雖然是對(duì)象,但是寄存在方法區(qū)里面),作為對(duì)方法區(qū)中這些數(shù)據(jù)的訪問(wèn)入口。

對(duì)(1),并沒(méi)有指明2進(jìn)制字節(jié)流的獲得途徑,也即不1定都是從1個(gè)Class文件中獲得,還可以從以下方式獲得:

    1)從緊縮包中獲得,比如 JAR包、EAR、WAR包等
    2)從網(wǎng)絡(luò)中獲得,比如紅極1時(shí)的Applet技術(shù)
    3)從運(yùn)行進(jìn)程中動(dòng)態(tài)生成,最出名的便是動(dòng)態(tài)代理技術(shù),在java.lang.reflect.Proxy 中,就是用了 ProxyGenerator.generateProxyClass 來(lái)為特定接口生成情勢(shì)為“$Proxy”的代理類的2進(jìn)制流
    4)從其它文件生成,如JSP文件生成Class 類
    5)從數(shù)據(jù)庫(kù)中讀取,比如說(shuō)有些中間件服務(wù)器,通過(guò)數(shù)據(jù)庫(kù)完成程序代碼在集群之間的分發(fā)

相對(duì)類加載進(jìn)程的其他階段,加載這1步驟是開發(fā)人員可控的,便可以通過(guò)自定義類加載器來(lái)控制加載進(jìn)程。

對(duì)數(shù)組來(lái)講,數(shù)組類本身不通過(guò)類加載器創(chuàng)建,它是由Java虛擬機(jī)直接創(chuàng)建的,但是數(shù)組的元素類型,終究是要靠類加載器去創(chuàng)建。

3.2 驗(yàn)證

驗(yàn)證階段的目的是為了確保Class文件的字節(jié)流中包括的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)本身的安全。
Java語(yǔ)言本身是相對(duì)安全的,由于使用純潔的java代碼沒(méi)法做到諸如訪問(wèn)數(shù)組邊界意外的數(shù)據(jù)、講1個(gè)對(duì)象轉(zhuǎn)型為它并未實(shí)現(xiàn)的類型、跳轉(zhuǎn)到不存在的代碼行之類的事情,如果我們這樣做了,那編譯器將謝絕編譯,也就保證了安全。但是前面說(shuō)過(guò),Class文件其實(shí)不1定要用Java源碼編譯而來(lái),它還可以從很多途徑產(chǎn)生,在字節(jié)碼層面,其他方式可能能做到j(luò)ava代碼沒(méi)法做到的事情,因此虛擬機(jī)需要對(duì)加載盡可能的字節(jié)流進(jìn)行驗(yàn)證。驗(yàn)證進(jìn)程分為4步:
(1)文件格式驗(yàn)證
這1階段是要驗(yàn)證字節(jié)流是不是符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理。包括以下這些驗(yàn)證點(diǎn):
    - 是不是以魔數(shù)0xCAFEBABE開頭
    - 主、次版本號(hào)是不是在當(dāng)前虛擬機(jī)處理范圍以內(nèi)
    - 常量池的常量中是不是有不被支持的常量類型(檢查常量tag標(biāo)志)
    - 指向常量的各種索引值中是不是有指向不存在的常量或不符合類型的常量
    - CONSTANT_Utf8_info 型的常量中是不是有不符合UTF8 編碼的數(shù)據(jù)
    - Class 文件中各個(gè)部份和文件本身是不是有被刪除的或被附加的其它信息
    ...
這1階段驗(yàn)證的目的是保證輸入的字節(jié)流能正確的解析并存儲(chǔ)到方法區(qū)中,這階段是基于2進(jìn)制字節(jié)流進(jìn)行的,通過(guò)驗(yàn)證后,字節(jié)流才會(huì)進(jìn)入到內(nèi)存的方法區(qū)中進(jìn)行存儲(chǔ)。因此,后面的3個(gè)驗(yàn)證階段是基于方法區(qū)的存儲(chǔ)結(jié)構(gòu)進(jìn)行分析的,不會(huì)再直接操作字節(jié)流了。

(2)元數(shù)據(jù)驗(yàn)證
對(duì)字節(jié)碼描寫的信息進(jìn)行語(yǔ)義分析,以保證其描寫的信息符合Java語(yǔ)言規(guī)范的要求,主要是驗(yàn)證類的繼承關(guān)系、數(shù)據(jù)類型是不是符合,驗(yàn)證點(diǎn)包括:
    - 這個(gè)類是不是有父類(除Object類外,其他所有類都應(yīng)當(dāng)有父類)
    - 這個(gè)類的父類是不是繼承了不允許被繼承的類(final 修飾的類)
    - 這個(gè)類如果不是抽象類,是不是實(shí)現(xiàn)了其父類或接口當(dāng)中要求實(shí)現(xiàn)的所有方法
    - 類中的字段、方法是不是和父類產(chǎn)生矛盾(如覆蓋了父類final 字段,出現(xiàn)了非法的方法重載,如方法參數(shù)1致,但返回類型卻不同)

(3)字節(jié)碼驗(yàn)證
最復(fù)雜的1個(gè)階段,主要目的是通過(guò)數(shù)據(jù)流和控制流分析,肯定程序語(yǔ)義是合法的、符合邏輯的。在元數(shù)據(jù)驗(yàn)證階段對(duì)數(shù)據(jù)類型做完校驗(yàn)后,這個(gè)階段將對(duì)類的方法體進(jìn)行校驗(yàn)分析,以保證被校驗(yàn)類的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的事件,有以下1些驗(yàn)證點(diǎn):
    - 保證任什么時(shí)候候,操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作,例如不會(huì)出現(xiàn)類似這樣的情況:在操作棧放入了1個(gè)int類型數(shù)據(jù),使用時(shí)卻按 long 類型加載到本地變量表中
    - 保證跳轉(zhuǎn)指令不會(huì)跳轉(zhuǎn)到方法體外的字節(jié)碼指令上
    - 保證方法體中類型轉(zhuǎn)換是有效的

(4)符號(hào)援用驗(yàn)證
這1階段產(chǎn)生在虛擬機(jī)將符號(hào)援用轉(zhuǎn)化為直接援用的時(shí)候,而這個(gè)轉(zhuǎn)化動(dòng)作產(chǎn)生在解析階段,符號(hào)援用可以看作是對(duì)類本身之外(常量池中的各種符號(hào)援用)的信息進(jìn)行匹配性校驗(yàn),驗(yàn)證點(diǎn)以下:
    - 符號(hào)援用中通過(guò)字符串描寫的全限定名是不是能找到相應(yīng)的類
    - 在指定類中對(duì)否存在符合方法的字段描寫符和簡(jiǎn)單名稱所描寫的方法和字段
    - 符號(hào)援用中的類、字段、方法的訪問(wèn)性(private、protected、public、default)是不是可被當(dāng)前類訪問(wèn)
這1階段驗(yàn)證的目的是確保解析動(dòng)作能正常履行。

對(duì)虛擬機(jī)來(lái)講,驗(yàn)證階段是1個(gè)非常重要的,但不是1定必要(由于對(duì)程序運(yùn)行期沒(méi)有影響)的的階段。

3.3 準(zhǔn)備

準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。有兩點(diǎn)需要注意:
(1)這階段進(jìn)行內(nèi)存分配的僅包括類變量(即被static修飾的變量),不包括實(shí)例變量,實(shí)例變量會(huì)在對(duì)象實(shí)例化時(shí)隨著對(duì)象1起分配在Java堆中
(2)這里所說(shuō)的初始值“通常情況”下是數(shù)據(jù)類型的零值,假定1個(gè)類變量的定義以下:
    public static int value = 123;
那變量value在準(zhǔn)備階段過(guò)后的零值為0而不是123,由于這時(shí)候候并未履行任何Java方法,把value賦值為123的動(dòng)作是在初始化階段才會(huì)進(jìn)行。對(duì)“非通常情況”,是指定義為常量的那些變量(即final修飾的),會(huì)在這1階段就被賦值,如:
    public static final int value = 123;
此時(shí)在準(zhǔn)備階段過(guò)后,value的值將會(huì)被賦值為123。

3.4 解析

解析階段是虛擬機(jī)將常量池中的符號(hào)援用轉(zhuǎn)化為直接援用的進(jìn)程。
    - 符號(hào)援用(Symbolic References):即用1組符號(hào)來(lái)描寫所援用的目標(biāo)。它與虛擬機(jī)的內(nèi)存布局無(wú)關(guān),援用的目標(biāo)不1定已加載到內(nèi)存中
    - 直接援用(Direct References):直接援用可以是指向目標(biāo)的指針、相對(duì)偏移量或是1個(gè)能間接定位到目標(biāo)的句柄。它是和虛擬機(jī)內(nèi)存布局相干的,如果有了直接援用,那援用的目標(biāo)一定已在內(nèi)存中存在了。
解析動(dòng)作主要針對(duì) 類或接口、字段、類方法、接口方法、方法類型、方法句柄 和 調(diào)用限定符 7類符號(hào)援用進(jìn)行。
(1)類或接口的解析
判斷所要轉(zhuǎn)化成的直接援用是對(duì)數(shù)組類型,還是普通的對(duì)象類型的援用,從而進(jìn)行不同的解析。
(2)字段解析
在對(duì)字段進(jìn)行解析前,會(huì)先查看該字段所屬的類或接口的符號(hào)援用是不是已解析過(guò),沒(méi)有就先對(duì)字段所屬的接口或類進(jìn)行解析。在對(duì)字段進(jìn)行解析的時(shí)候,先查找本類或接口中是不是有該字段,有就直接返回;否則,再對(duì)實(shí)現(xiàn)的接口進(jìn)行遍歷,會(huì)依照繼承關(guān)系從下往上遞歸(也就是說(shuō),每一個(gè)父接口都會(huì)走1遍)搜索各個(gè)接口和它的父接口,返回最近1個(gè)接口的直接援用;再對(duì)繼承的父類進(jìn)行遍歷,會(huì)依照繼承關(guān)系從下往上遞歸(也就是說(shuō),每一個(gè)父類都會(huì)走1遍)搜索各個(gè)父類,返回最近1個(gè)父類的直接援用。
(3)類方法解析
和字段解析搜索步驟差不多,只不過(guò)是先搜索父類,再搜索接口。
(4)接口方法解析
和類方法解析差不多,只不過(guò)接口中不會(huì)有父類,因此只需要對(duì)父接口進(jìn)行搜索便可。

3.5 初始化

初始化是類加載進(jìn)程的最后1步,此階段才開始真正履行類中定義的Java程序代碼(或說(shuō)字節(jié)碼,也僅限與履行<clinit>()方法)。在準(zhǔn)備階段,我們已給變量付過(guò)1次系統(tǒng)要求的初始值(零值)而在初始化階段,則會(huì)根據(jù)程序員的意愿給類變量和其他資源賦值。主要是通過(guò)<clinit>()方法來(lái)履行的:
 (1)<clinit>()方法是由編譯器自動(dòng)搜集類中的所有類變量的賦值動(dòng)作靜態(tài)語(yǔ)句塊中的語(yǔ)句合并產(chǎn)生的,編譯器搜集的順序是由語(yǔ)句在源文件中出現(xiàn)的順序所決定的,靜態(tài)語(yǔ)句塊中只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量定義在它以后的變量,在前面的靜態(tài)語(yǔ)句中可以賦值,但是不能訪問(wèn)以下:
public class Test { static{ i = 0;//可以給變量賦值,編譯通過(guò) System.out.println(i);//編譯不通過(guò)!!不能進(jìn)行訪問(wèn)后面的靜態(tài)變量 } static int i =1; }
有點(diǎn)與我們平常的認(rèn)知相反,這里是可以下賦值,卻不能訪問(wèn)...

 (2)<clinit>()方法與實(shí)例構(gòu)造器<init>()方法(類的構(gòu)造函數(shù))不同,它不需要顯式地調(diào)用父類構(gòu)造器,虛擬機(jī)會(huì)保證在子類的<clinit>()方法履行之前,父類的<clinit>()方法已履行終了。因此,在虛擬機(jī)中第1個(gè)被履行的<clinit>()方法的類肯定是java.lang.Object

 (3)<clinit>()方法對(duì)類或接口來(lái)講其實(shí)不是必須的,如果1個(gè)類中沒(méi)有靜態(tài)語(yǔ)句塊,也沒(méi)有對(duì)類變量的賦值操作,那末編譯器可以不為這個(gè)類生成<clinit>()方法。

 (4)接口中不能使用靜態(tài)語(yǔ)句塊,但依然有類變量(final static)初始化的賦值操作,因此接口與類1樣會(huì)生成<clinit>()方法。但是接口與類不同的是:履行接口的<clinit>()方法不需要先履行父接口的<clinit>()方法,只有當(dāng)父接口中定義的變量被使用時(shí),父接口才會(huì)被初始化。另外,接口的實(shí)現(xiàn)類在初始化時(shí)也1樣不會(huì)履行接口的<clinit>()方法。

 (5)虛擬機(jī)會(huì)保證1個(gè)類的<clinit>()方法在多線程環(huán)境中被正確地加鎖和同步,如果多個(gè)線程同時(shí)去初始化1個(gè)類,那末只會(huì)有1個(gè)線程去履行這個(gè)類的<clinit>()方法,其他線程都需要阻塞等待,直到活動(dòng)線程履行<clinit>()方法終了。如果在1個(gè)類的<clinit>()方法中有耗時(shí)很長(zhǎng)的操作,那便可能造成多個(gè)線程阻塞,在實(shí)際利用中這類阻塞常常是很隱蔽的。

4、類加載器

前面說(shuō)過(guò),在類加載進(jìn)程的第1個(gè)階段:加載階段,除可使用系統(tǒng)提供的引導(dǎo)類加載器外,還可使用用戶自定義的類加載器,以便讓用戶決定如何去獲得所需要的類(是從Class文件中?還是從jar、或其他方式...可以自由決定)。

4.1 類和類加載器

任意1個(gè)類,都需要由加載它的類加載器這個(gè)類本身共同肯定其在Java 虛擬機(jī)中的唯1性,每個(gè)類加載器,都具有1個(gè)獨(dú)立的類名稱空間。這句話可以表達(dá)的更通俗1些:比較兩個(gè)類是不是相等,只有在這兩個(gè)類是同1個(gè)類加載器加載的條件下才意義。否則,即便這兩個(gè)類來(lái)自同1個(gè)Class文件,被同1個(gè)虛擬機(jī)加載,但只要加載他們的類加載器不同,那這兩個(gè)類就一定不相等

這里的“相等”,包括代表類的 Class 對(duì)象的equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回結(jié)果,也包括 instanceof 關(guān)鍵字對(duì)對(duì)象所屬關(guān)系判定等情況。下面代碼演示了不同類加載器對(duì) instanceof 關(guān)鍵字運(yùn)算的結(jié)果的影響。

public class ClassLoaderTest { public static void main(String[] args) throws Exception { ClassLoader myLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } }; Class c = myLoader.loadClass("org.bupt.xiaoye.blog.ClassLoaderTest"); Object obj = c.newInstance(); System.out.println(obj.getClass()); System.out.println(ClassLoaderTest.class); System.out.println(obj instanceof ClassLoaderTest); } }
運(yùn)行結(jié)果以下:
class org.bupt.xiaoye.blog.ClassLoaderTest  
class org.bupt.xiaoye.blog.ClassLoaderTest  
false
我們使用了1個(gè)自定義的類加載器去加載ClassLoaderTest,由第1句也能夠看出這個(gè)對(duì)象也的確是ClassLoaderTest實(shí)例化出來(lái)的對(duì)象,但是這個(gè)對(duì)象在與類class org.bupt.xiaoye.blog.ClassLoaderTest 做屬性檢查的時(shí)候卻反悔了false,這就是由于虛擬機(jī)中存在了兩個(gè)ClassLoaderTest類,1個(gè)由系統(tǒng)利用程序類加載器加載,1個(gè)由我們自定義的類加載器加載,雖然是 來(lái)自同1個(gè)Class文件,但仍然是兩個(gè)獨(dú)立的類

因此,類是不是相等,取決于類本身加載該類的類加載器是不是是同1個(gè)類加載器

4.2 雙親委派模型

從虛擬機(jī)的角度來(lái)說(shuō),只存在兩種不同的類加載器:

    1種是啟動(dòng)類加載器(Bootstrap ClassLoader),這個(gè)類加載器用 C++  語(yǔ)言實(shí)現(xiàn), 是虛擬機(jī)本身的1部份:

    另外一種就是所有其它的類加載器, 這些類加載器用Java 語(yǔ)言實(shí)現(xiàn),獨(dú)立于虛擬機(jī)外部,并且全都繼承與抽象類 java.lang.ClassLoader。

從Java 開發(fā)人員的角度來(lái)看,類加載器還可以劃分的更細(xì)致1些,絕大多數(shù)Java 程序都會(huì)用到以下3種系統(tǒng)提供的類加載器:

   (1)啟動(dòng)類加載器(Bootstrap ClassLoader) : 這個(gè)類加載器負(fù)責(zé)將寄存在 <JAVA_HOME>\lib 目錄中的,或被 -Xbootclasspath 參數(shù)指定的路徑中的,并且是虛擬機(jī)辨認(rèn)的(僅依照文件名辨認(rèn),如rt.jar ,名字不符合類庫(kù)不會(huì)加載) 類庫(kù)加載到虛擬機(jī)內(nèi)存中。啟動(dòng)類加載器沒(méi)法被 java 程序直接援用,如需要,直接使用 null 代替便可。
   (2)擴(kuò)大類加載器(Extension ClassLoader):這個(gè)加載器由sun.misc.Launcher$ExtClassLoader 實(shí)現(xiàn),它負(fù)責(zé)加載<JAVA_HOME>\lib\ext 目錄中的,或被 java.ext.dirs 系統(tǒng)變量所指定的路徑中的所有類庫(kù),開發(fā)者可以直接使用擴(kuò)大類加載器。
   (3)利用程序類加載器(Application ClassLoader):這個(gè)類加載器由 sun.misc.Launcher$AppClassLoader 實(shí)現(xiàn)。這個(gè)這個(gè)類加載器是 ClassLoader 中的getSystemClassLoader() 方法的返回值,所以1般稱它為系統(tǒng)類加載器。它負(fù)責(zé)加載用戶路徑(ClassPath)上所指定的類庫(kù),開發(fā)者可使用這個(gè)類加載器,如果利用程序沒(méi)有自定義過(guò)自己的類加載器,1般情況下這個(gè)就是程序中默許的類加載器

我們的利用程序都是由這3中類加載器相互配合進(jìn)行加載的,如果有必要,還可以加入自己定義的類加載器。這些類加載器之間的關(guān)系1般以下圖所示:


圖中的類加載器之間的這類層次關(guān)系,稱為類加載器的雙親委派模型。雙親委派模型要求除頂層的啟動(dòng)類加載器,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。這里類加載器之間的父子關(guān)系1般不會(huì)以繼承關(guān)系來(lái)實(shí)現(xiàn),而是使用組合關(guān)系來(lái)復(fù)用父加載器的代碼。
雙親委派模型的工作進(jìn)程是如果1個(gè)類加載器收到了類加載器的要求,它首先不會(huì)自己嘗試加載這個(gè)類,而是把這個(gè)要求委派給父類加載器去完成,每個(gè)層次的類加載器都是如此,因此所有的加載要求終究都應(yīng)當(dāng)傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父類加載器反饋?zhàn)约簺](méi)法完成這個(gè)加載要求(它的搜索范圍中沒(méi)有找到所需的類時(shí)),子加載類才會(huì)嘗試自己去加載

使用雙親委派模型的好處:就是Java類隨著它的類加載器1起具有了1種帶有優(yōu)先級(jí)的層次關(guān)系。比如對(duì)類Object來(lái)講,它寄存在rt.jar中,不管哪個(gè)類加載器要加載這個(gè)類,終究都是委派給處于模型最頂真?zhèn)€啟動(dòng)類加載器去加載,因此Object類在程序中的各種類加載器環(huán)境中都是同1個(gè)類。相反,如果沒(méi)有使用雙親委派模型,由各個(gè)類自己去加載的話,依照我們前面說(shuō)的,如果用戶自己編寫了1個(gè)Object類,并放在程序的ClassPath中,那系統(tǒng)中將會(huì)出現(xiàn)多個(gè)不同的Object類,此時(shí)Java類型提示中最基礎(chǔ)的行動(dòng)也就沒(méi)法保證了,利用程序也將變得混亂。

因此,雙親委派模型對(duì)保證Java程序的穩(wěn)定運(yùn)作很重要,但是他的實(shí)現(xiàn)其實(shí)很簡(jiǎn)單,實(shí)現(xiàn)雙親委派模型的代碼幾種在java.lang.ClassLoader的loadClass()方法當(dāng)中,邏輯清晰易懂:先檢查類是不是被加載過(guò),若沒(méi)有則調(diào)用父加載器的loadClass() 方法,若父加載器為空則默許使用啟動(dòng)類加載器作為父加載器。如果父加載器失敗,拋出 ClassNotFoundException 異常后,再調(diào)用自己的 finClass() 方法進(jìn)行加載,以下:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先檢查類是不是已被加載過(guò) Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { // 調(diào)用父類加載器加載 c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. //父類加載器沒(méi)法完成加載,調(diào)用本身的加載器加載 long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime( t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom( t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }

(注:文中圖片來(lái)源于:
http://blog.csdn.net/zq602316498/article/details/38871785
http://blog.csdn.net/zq602316498/article/details/38902355


生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 欧美一级在线观看 | av另类| 免费在线观看一区 | 中文av电影 | 麻豆精品久久久 | 日本不卡高清视频 | 不卡免费视频 | www.精品 | 国产伦精品一区 | 欧美日韩福利 | 最新日韩在线观看视频 | 欧美视频网 | 国产黄一级| 国产一区二区在线观看免费 | 欧美激情综合五月色丁香小说 | 91免费国产在线 | www久久| 国产黄色在线 | 黑人中文字幕一区二区三区 | 黄色电影在线免费看 | 久久久国产精 | 色在线视频 | 欧美激情欧美激情在线五月 | 在线看国产 | 国产一区二区三区四区三区四 | 欧美日一区二区 | 欧洲黄色网 | 免费九九视频 | 狠狠影院| 日韩在线视频二区 | 国产精品黄 | 欧美综合视频 | 亚洲一区视频在线 | 国产在线看h | 久久精品免费电影 | 色999视频| 国产高清一区二区三区 | 一区二区三区在线视频播放 | 日本免费三片免费观看 | 精品欧美一区二区三区在线观看 | 欧美一区二区精品 |