Android插件化开发基础之Java类加载器与双亲委派模型
類加載器
Java虛擬機(jī)類加載過程是把Class類文件加載到內(nèi)存,并對(duì)Class文件中的數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的java類型的過程。
在加載階段,java虛擬機(jī)需要完成以下3件事:
a.通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
b.將定義類的二進(jìn)制字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
c.在java堆中生成一個(gè)代表該類的java.lang.Class對(duì)象,作為方法區(qū)數(shù)據(jù)的訪問入口。
而類的加載過程是通過類加載器完成的。
在Java中,任意一個(gè)類都需要由加載它的類加載器和這個(gè)類本身一同確定其在java虛擬機(jī)中的唯一性,即比較兩個(gè)類是否相等,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提之下才有意義,否則,即使這兩個(gè)類來源于同一個(gè)Class類文件,只要加載它的類加載器不相同,那么這兩個(gè)類必定不相等(這里的相等包括代表類的Class對(duì)象的equals()方法、isAssignableFrom()方法、isInstance()方法和instanceof關(guān)鍵字的結(jié)果)。
加載類的開放性
類加載器(ClassLoader)是Java語言的一項(xiàng)創(chuàng)新,也是Java流行的一個(gè)重要原因。在類加載的第一階段“加載”過程中,需要通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流,完成這個(gè)動(dòng)作的代碼塊就是類加載器。這一動(dòng)作是放在Java虛擬機(jī)外部去實(shí)現(xiàn)的,以便讓應(yīng)用程序自己決定如何獲取所需的類。
虛擬機(jī)規(guī)范并沒有指明二進(jìn)制字節(jié)流要從一個(gè)Class文件獲取,或者說根本沒有指明從哪里獲取、怎樣獲取。這種開放使得Java在很多領(lǐng)域得到充分運(yùn)用,例如:
- 從ZIP包中讀取,這很常見,成為JAR,EAR,WAR格式的基礎(chǔ)
- 從網(wǎng)絡(luò)中獲取,最典型的應(yīng)用就是Applet
- 運(yùn)行時(shí)計(jì)算生成,最典型的是動(dòng)態(tài)代理技術(shù),在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass來為特定接口生成形式為“*$Proxy”的代理類的二進(jìn)制字節(jié)流
- 有其他文件生成,最典型的JSP應(yīng)用,由JSP文件生成對(duì)應(yīng)的Class類?
……
類加載器與類的唯一性
類加載器雖然只用于實(shí)現(xiàn)類的加載動(dòng)作,但是對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身共同確立其在Java虛擬機(jī)中的唯一性。通俗的說,JVM中兩個(gè)類是否“相等”,首先就必須是同一個(gè)類加載器加載的,否則,即使這兩個(gè)類來源于同一個(gè)Class文件,被同一個(gè)虛擬機(jī)加載,只要類加載器不同,那么這兩個(gè)類必定是不相等的。
這里的“相等”,包括代表類的Class對(duì)象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回結(jié)果,也包括使用instanceof關(guān)鍵字做對(duì)象所屬關(guān)系判定等情況。
以下代碼說明了不同的類加載器對(duì)instanceof關(guān)鍵字運(yùn)算的結(jié)果的影響。
package com.jvm.classloading;import java.io.IOException; import java.io.InputStream;/*** 類加載器在類相等判斷中的影響* * instanceof關(guān)鍵字* */public class ClassLoaderTest {public static void main(String[] args) throws Exception {// 自定義類加載器ClassLoader myLoader = new ClassLoader() {@Overridepublic 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(fileName);}byte[] b = new byte[is.available()];is.read(b);return defineClass(name, b, 0, b.length); } catch (IOException e) {throw new ClassNotFoundException();}}};// 使用ClassLoaderTest的類加載器加載本類Object obj1 = ClassLoaderTest.class.getClassLoader().loadClass("com.jvm.classloading.ClassLoaderTest").newInstance();System.out.println(obj1.getClass());System.out.println(obj1 instanceof com.jvm.classloading.ClassLoaderTest);// 使用自定義類加載器加載本類Object obj2 = myLoader.loadClass("com.jvm.classloading.ClassLoaderTest").newInstance();System.out.println(obj2.getClass());System.out.println(obj2 instanceof com.jvm.classloading.ClassLoaderTest);} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
輸出結(jié)果:
class com.jvm.classloading.ClassLoaderTest true class com.jvm.classloading.ClassLoaderTest false- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
myLoader是自定義的類加載器,可以用來加載與自己在同一路徑下的Class文件。main函數(shù)的第一部分使用系統(tǒng)加載主類ClassLoaderTest的類加載器加載ClassLoaderTest,輸出顯示,obj1的所屬類型檢查正確,這是虛擬機(jī)中有2個(gè)ClassLoaderTest類,一個(gè)是主類,另一個(gè)是main()方法中加載的類,由于這兩個(gè)類使用同一個(gè)類加載器加載并且來源于同一個(gè)Class文件,因此這兩個(gè)類是完全相同的。
第二部分使用自定義的類加載器加載ClassLoaderTest,class com.jvm.classloading.ClassLoderTest顯示,obj2確實(shí)是類com.jvm.classloading.ClassLoaderTest實(shí)例化出來的對(duì)象,但是第二句輸出false。此時(shí)虛擬機(jī)中有3個(gè)ClassLoaderTest類,由于第3個(gè)類的類加載器與前面2個(gè)類加載器不同,雖然來源于同一個(gè)Class文件,但它是一個(gè)獨(dú)立的類,所屬類型檢查是返回結(jié)果自然是false。
雙親委派模型
類加載器種類
從Java虛擬機(jī)的角度來說,只存在兩種不同的類加載器:一種是啟動(dòng)類加載器(Bootstrap ClassLoader),這個(gè)類加載器使用C++語言實(shí)現(xiàn)(HotSpot虛擬機(jī)中),是虛擬機(jī)自身的一部分;另一種就是所有其他的類加載器,這些類加載器都有Java語言實(shí)現(xiàn),獨(dú)立于虛擬機(jī)外部,并且全部繼承自java.lang.ClassLoader。
從開發(fā)者的角度,類加載器可以細(xì)分為:
-
啟動(dòng)(Bootstrap)類加載器:負(fù)責(zé)將 Java_Home/lib下面的類庫加載到內(nèi)存中(比如rt.jar)。由于引導(dǎo)類加載器涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),開發(fā)者無法直接獲取到啟動(dòng)類加載器的引用,所以不允許直接通過引用進(jìn)行操作。
-
標(biāo)準(zhǔn)擴(kuò)展(Extension)類加載器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實(shí)現(xiàn)的。它負(fù)責(zé)將Java_Home /lib/ext或者由系統(tǒng)變量 java.ext.dir指定位置中的類庫加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。
-
應(yīng)用程序(Application)類加載器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實(shí)現(xiàn)的。它負(fù)責(zé)將系統(tǒng)類路徑(CLASSPATH)中指定的類庫加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器。由于這個(gè)類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般稱為系統(tǒng)(System)加載器。
除此之外,還有自定義的類加載器,它們之間的層次關(guān)系被稱為類加載器的雙親委派模型。該模型要求除了頂層的啟動(dòng)類加載器外,其余的類加載器都應(yīng)該有自己的父類加載器,而這種父子關(guān)系一般通過組合(Composition)關(guān)系來實(shí)現(xiàn),而不是通過繼承(Inheritance)。
雙親委派模型
雙親委派模型過程
某個(gè)特定的類加載器在接到加載類的請(qǐng)求時(shí),首先將加載任務(wù)委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載。
使用雙親委派模型的好處在于Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。例如類java.lang.Object,它存在在rt.jar中,無論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型最頂端的Bootstrap ClassLoader進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個(gè)類。相反,如果沒有雙親委派模型而是由各個(gè)類加載器自行加載的話,如果用戶編寫了一個(gè)java.lang.Object的同名類并放在ClassPath中,那系統(tǒng)中將會(huì)出現(xiàn)多個(gè)不同的Object類,程序?qū)⒒靵y。因此,如果開發(fā)者嘗試編寫一個(gè)與rt.jar類庫中重名的Java類,可以正常編譯,但是永遠(yuǎn)無法被加載運(yùn)行。
雙親委派模型的系統(tǒng)實(shí)現(xiàn)
在java.lang.ClassLoader的loadClass()方法中,先檢查是否已經(jīng)被加載過,若沒有加載則調(diào)用父類加載器的loadClass()方法,若父加載器為空則默認(rèn)使用啟動(dòng)類加載器作為父加載器。如果父加載失敗,則拋出ClassNotFoundException異常后,再調(diào)用自己的findClass()方法進(jìn)行加載。
protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{//check the class has been loaded or notClass c = findLoadedClass(name);if(c == null){try{if(parent != null){c = parent.loadClass(name,false);}else{c = findBootstrapClassOrNull(name);}}catch(ClassNotFoundException e){//if throws the exception ,the father can not complete the load}if(c == null){c = findClass(name);}}if(resolve){resolveClass(c);}return c; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
注意,雙親委派模型是Java設(shè)計(jì)者推薦給開發(fā)者的類加載器的實(shí)現(xiàn)方式,并不是強(qiáng)制規(guī)定的。大多數(shù)的類加載器都遵循這個(gè)模型,但是JDK中也有較大規(guī)模破壞雙親模型的情況,例如線程上下文類加載器(Thread Context ClassLoader)的出現(xiàn),具體分析可以參見周志明著《深入理解Java虛擬機(jī)》。
參考?
1、周志明,深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐,機(jī)械工業(yè)出版社?
2、Alexia(minmin)博客,http://www.cnblogs.com/lanxuezaipiao/p/4138511.html
總結(jié)
以上是生活随笔為你收集整理的Android插件化开发基础之Java类加载器与双亲委派模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android插件化开发之动态加载技术系
- 下一篇: JavaJVM之ClassLoader源