JVM是如何知道java.lang包中的類的?JVM又是如何知道我們應(yīng)用中的類的?我們的應(yīng)用中明明是有某個(gè)類, 但是JVM卻拋出ClassNotFoundException,這是為什么?XxxImpl類已經(jīng)實(shí)現(xiàn)了接口Xxx,但是卻拋出XxxImpl does not extend from Xxx,這是為什么?使用類型轉(zhuǎn)換時(shí),可能會(huì)拋出 aa.bb.cc.XXX can not cast to aa.bb.cc,這又是為什么?等等諸多看似詭異情況,其實(shí)都是因?yàn)镃lassLoader。
了解反射的人都知道每一個(gè)類都有一個(gè)Class對(duì)象與之對(duì)應(yīng),那么這個(gè)Class對(duì)象又是哪來(lái)的呢?
如果要了解Class的基本知識(shí),可以參考:
http://blog.csdn.net/irelandken/article/details/7048817http://tyrion.VEvb.com/blog/1958814http://www.blogjava.net/lihuaxajh/articles/94371.htmlhttp://www.javaworld.com/article/2077332/core-java/get-a-load-of-that-name.htmlhttp://www.javaworld.com/article/2075796/java-platform/java-101--class-and-object-initialization.html我對(duì)ClassLoader的理解
下面是我看了這些博客、文章后,并自定義了一個(gè)ClassLoader進(jìn)行測(cè)試后的理解:
1)ClassLoader加載一個(gè)類時(shí),不會(huì)加載類中所有的類,而是在運(yùn)行時(shí)根據(jù)要使用的類動(dòng)態(tài)加載的,即按需加載。
2)ClassLoader加載一個(gè)類時(shí),也會(huì)加載其父類(包括接口)
3)當(dāng)前類A在運(yùn)行時(shí),類中要使用的其它的類(B,C,D類等),默認(rèn)是由當(dāng)前類A的加載器來(lái)加載的。
4)類型強(qiáng)制轉(zhuǎn)換時(shí),先判定是不是同一個(gè)類加載器,如果不是,就不能進(jìn)行轉(zhuǎn)換。一個(gè)對(duì)象A但往由BootstrapCLassLoader加載的類(A的父類或者接口)轉(zhuǎn)換時(shí),好像不遵循這個(gè)過(guò)程。
5)Thread#contextClassLoader可以用于在切換類加載器。
6)默認(rèn)情況下,線程的上下文加載器采用的與父線程的上下文加載器是同一個(gè)。
7)Class.forName(“xxx”)、Xxx.class 的加載器,使用的都是當(dāng)前類的類加載器。
下面是自定義類加載器代碼清單:
下面就對(duì)這些類做一個(gè)簡(jiǎn)單的說(shuō)明:
StringUtil.java
StringUtil中只有兩個(gè)方法,用于判斷字符串空值。寫這個(gè)類實(shí)在是沒(méi)有必要,但是在測(cè)試中也可以看到效果的。
DebugUtil.java
只是為了打出一些信息。
Person.java
這是一個(gè)典型的Java Bean,沒(méi)有什么可說(shuō)的。
ClassLoaderTestRunner.java
這是業(yè)務(wù)類。從代碼上看也是很簡(jiǎn)單的,只是加載了Person類,創(chuàng)建一個(gè)Person對(duì)象,輸出一些信息。
這個(gè)類 既可以使用系統(tǒng)默認(rèn)的類加載器加載測(cè)試(main方法),也可以使用自定義的類加載器來(lái)測(cè)試。
自定義ClassLoader目前常用的加載機(jī)制有兩種:委托加載機(jī)制、子類優(yōu)先加載
1)委托加載機(jī)制loadClass()的流程:
(1) 判斷是否已經(jīng)加載這個(gè)類
(2) 如果沒(méi)有加載,當(dāng)前類加載器的父加載器加載,即執(zhí)行l(wèi)oadClass()方法。
(3) 如果當(dāng)前類加載器的所有父加載器都沒(méi)有加載到就讓當(dāng)前加載器調(diào)用 findClass()加載。
2)子類優(yōu)先加載器的流程:
(1) 判斷是否已經(jīng)加載這個(gè)類
(2) 當(dāng)前類加載器直接加載類
(3) 加載不到才走父類加載流程。
JDK中默認(rèn)的加載方式是1),也就是委托加載機(jī)制。
自定義類加載器,一般會(huì)重寫loadClass、findClass。

delegate字段是用于指定是否使用默認(rèn)的委托加載機(jī)制。

這是自定義的加載類的方法。流程是:
1)判斷該加載器是否已經(jīng)加載了這個(gè)類,為了避免重復(fù)的加載。
2)如果delegate=true,委托加載,就使用默認(rèn)的加載方式。
3)如果delegate=false, 排除受保護(hù)的類,然后使用下面的自定義的findClass來(lái)加載類。上述過(guò)程如果出現(xiàn)了異常,仍然會(huì)使用默認(rèn)的加載方法。

findClass是根據(jù)類名加載并定義一個(gè)Class對(duì)象。這個(gè)方法寫的很粗糙,只是為了測(cè)試ClassLoader。
接下來(lái)就可以使用ClassLoaderTest.java來(lái)測(cè)試了:

關(guān)于這個(gè)測(cè)試的簡(jiǎn)單說(shuō)明:
1)這個(gè)測(cè)試中的相關(guān)類,如String, UrlClassLoader, ClassLoader,Object, Thread,Runnable,Exception等都是由默認(rèn)的系統(tǒng)加載器(SystemClassLoader)或者其父類(ExtClassLoader、BootstrapClassLoader)來(lái)加載的。當(dāng)前這個(gè)運(yùn)行時(shí),這個(gè)類(ClassLoaderTest)的加載器是SystemClassLoader。
2)加載ClassLoaderTestRunner類時(shí),使用的自定義的類加載器(也就是SystemClassLoader的子加載器)。加載ClassLoaderTestRunner類時(shí),還會(huì)加載其父類,也就是Object類和Runable接口。
3)這個(gè)測(cè)試執(zhí)行的過(guò)程:
(1) 創(chuàng)建一個(gè)自定義加載器實(shí)例,指定其父加載器為系統(tǒng)加載器。
(2) 加載類ClassLoaderTestRunner。
(3) 啟動(dòng)一個(gè)線程執(zhí)行相關(guān)任務(wù)。
4)根據(jù)上述所說(shuō),如果將Object task = myAppClassLoader .loadClass( classFullName) . newInstance();
改為:
ClassLoaderRunner task= (ClassLoaderRunner) myAppClassLoader. loadClass( classFullName ) . newInstance();就會(huì)出錯(cuò)。
ClassLoaderRunner 是系統(tǒng)加載器加載的,myAppClassLoader是一個(gè)自定義加載器。所以兩者不能進(jìn)行類型的轉(zhuǎn)換。
如果直接在這個(gè)類中使用自定義類加載器來(lái)加載Person類,也會(huì)出現(xiàn)錯(cuò)誤。
如果有興趣的話,可以根據(jù)我上面總結(jié)的內(nèi)容,已經(jīng)我的這個(gè)測(cè)試,自己分析一下子線程t執(zhí)行過(guò)程中,哪些類是被自定義的類加載器加載的,哪些類是被系統(tǒng)加載器加載的,上述task執(zhí)行的結(jié)果是什么?
如果有不理解的也可以參考:
參考
http://blog.csdn.net/irelandken/article/details/7048817
http://tyrion.VEvb.com/blog/1958814
http://www.blogjava.net/lihuaxajh/articles/94371.html
http://www.javaworld.com/article/2077332/core-java/get-a-load-of-that-name.html
http://www.javaworld.com/article/2075796/java-platform/java-101--class-and-object-initialization.html
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注