日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

深入探讨 Java 类加载器(一)

發(fā)布時間:2023/12/31 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入探讨 Java 类加载器(一) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

類加載器是 Java 語言的一個創(chuàng)新,也是 Java 語言流行的重要原因之一。它使得 Java 類可以被動態(tài)加載到 Java 虛擬機中并執(zhí)行。類加載器從 JDK 1.0 就出現(xiàn)了,最初是為了滿足 Java Applet 的需要而開發(fā)出來的。Java Applet 需要從遠程下載 Java 類文件到瀏覽器中并執(zhí)行。現(xiàn)在類加載器在 Web 容器和 OSGi 中得到了廣泛的使用。一般來說,Java 應(yīng)用的開發(fā)人員不需要直接同類加載器進行交互。Java 虛擬機默認的行為就已經(jīng)足夠滿足大多數(shù)情況的需求了。不過如果遇到了需要與類加載器進行交互的情況,而對類加載器的機制又不是很了解的話,就很容易花大量 的時間去調(diào)試 ClassNotFoundException和 NoClassDefFoundError等異常。本文將詳細介紹 Java 的類加載器,幫助讀者深刻理解 Java 語言中的這個重要概念。下面首先介紹一些相關(guān)的基本概念。

類加載器基本概念

顧 名思義,類加載器(class loader)用來加載 Java 類到 Java 虛擬機中。一般來說,Java 虛擬機使用 Java 類的方式如下:Java 源程序(.java 文件)在經(jīng)過 Java 編譯器編譯之后就被轉(zhuǎn)換成 Java 字節(jié)代碼(.class 文件)。類加載器負責(zé)讀取 Java 字節(jié)代碼,并轉(zhuǎn)換成 java.lang.Class類的一個實例。每個這樣的實例用來表示一個 Java 類。通過此實例的 newInstance()方法就可以創(chuàng)建出該類的一個對象。實際的情況可能更加復(fù)雜,比如 Java 字節(jié)代碼可能是通過工具動態(tài)生成的,也可能是通過網(wǎng)絡(luò)下載的。

基本上所有的類加載器都是 java.lang.ClassLoader類的一個實例。下面詳細介紹這個 Java 類。

java.lang.ClassLoader類介紹

java.lang.ClassLoader類的基本職責(zé)就是根據(jù)一個指定的類的名稱,找到或者生成其對應(yīng)的字節(jié)代碼,然后從這些字節(jié)代碼中定義出一個 Java 類,即 java.lang.Class類的一個實例。除此之外,ClassLoader還負責(zé)加載 Java 應(yīng)用所需的資源,如圖像文件和配置文件等。不過本文只討論其加載類的功能。為了完成加載類的這個職責(zé),ClassLoader提供了一系列的方法,比較重要的方法如 表 1所示。關(guān)于這些方法的細節(jié)會在下面進行介紹。

表 1. ClassLoader 中與加載類相關(guān)的方法
方法說明
getParent()返回該類加載器的父類加載器。
loadClass(String name)加載名稱為 name的類,返回的結(jié)果是 java.lang.Class類的實例。
findClass(String name)查找名稱為 name的類,返回的結(jié)果是 java.lang.Class類的實例。
findLoadedClass(String name)查找名稱為 name的已經(jīng)被加載過的類,返回的結(jié)果是 java.lang.Class類的實例。
defineClass(String name, byte[] b, int off, int len)把字節(jié)數(shù)組 b中的內(nèi)容轉(zhuǎn)換成 Java 類,返回的結(jié)果是 java.lang.Class類的實例。這個方法被聲明為 final的。
resolveClass(Class<?> c)鏈接指定的 Java 類。

對于 表 1中給出的方法,表示類名稱的 name參數(shù)的值是類的二進制名稱。需要注意的是內(nèi)部類的表示,如 com.example.Sample$1和 com.example.Sample$Inner等表示方式。這些方法會在下面介紹類加載器的工作機制時,做進一步的說明。下面介紹類加載器的樹狀組織結(jié)構(gòu)。

類加載器的樹狀組織結(jié)構(gòu)

Java 中的類加載器大致可以分成兩類,一類是系統(tǒng)提供的,另外一類則是由 Java 應(yīng)用開發(fā)人員編寫的。系統(tǒng)提供的類加載器主要有下面三個:

  • 引導(dǎo)類加載器(bootstrap class loader):它用來加載 Java 的核心庫,是用原生代碼來實現(xiàn)的,并不繼承自 java.lang.ClassLoader。

  • 擴展類加載器(extensions class loader):它用來加載 Java 的擴展庫。Java 虛擬機的實現(xiàn)會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載 Java 類。

  • 系統(tǒng)類加載器(system class loader):它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH)來加載 Java 類。一般來說,Java 應(yīng)用的類都是由它來完成加載的。可以通過 ClassLoader.getSystemClassLoader()來獲取它。

除了系統(tǒng)提供的類加載器以外,開發(fā)人員可以通過繼承 java.lang.ClassLoader類的方式實現(xiàn)自己的類加載器,以滿足一些特殊的需求。

除了引導(dǎo)類加載器之外,所有的類加載器都有一個父類加載器。通過 表 1中給出的 getParent()方 法可以得到。對于系統(tǒng)提供的類加載器來說,系統(tǒng)類加載器的父類加載器是擴展類加載器,而擴展類加載器的父類加載器是引導(dǎo)類加載器;對于開發(fā)人員編寫的類加 載器來說,其父類加載器是加載此類加載器 Java 類的類加載器。因為類加載器 Java 類如同其它的 Java 類一樣,也是要由類加載器來加載的。一般來說,開發(fā)人員編寫的類加載器的父類加載器是系統(tǒng)類加載器。類加載器通過這種方式組織起來,形成樹狀結(jié)構(gòu)。樹的根 節(jié)點就是引導(dǎo)類加載器。圖 1中給出了一個典型的類加載器樹狀組織結(jié)構(gòu)示意圖,其中的箭頭指向的是父類加載器。

圖 1. 類加載器樹狀組織結(jié)構(gòu)示意圖

代碼清單 1演示了類加載器的樹狀組織結(jié)構(gòu)。

清單 1. 演示類加載器的樹狀組織結(jié)構(gòu)
?public?class?ClassLoaderTree?{?public?static?void?main(String[]?args)?{?ClassLoader?loader?=?ClassLoaderTree.class.getClassLoader();?while?(loader?!=?null)?{?System.out.println(loader.toString());?loader?=?loader.getParent();?}?}?}

每個 Java 類都維護著一個指向定義它的類加載器的引用,通過 getClassLoader()方法就可以獲取到此引用。代碼清單 1中通過遞歸調(diào)用 getParent()方法來輸出全部的父類加載器。代碼清單 1的運行結(jié)果如 代碼清單 2所示。

清單 2. 演示類加載器的樹狀組織結(jié)構(gòu)的運行結(jié)果
?sun.misc.Launcher$AppClassLoader@9304b1?sun.misc.Launcher$ExtClassLoader@190d11

如 代碼清單 2所示,第一個輸出的是 ClassLoaderTree類的類加載器,即系統(tǒng)類加載器。它是 sun.misc.Launcher$AppClassLoader類的實例;第二個輸出的是擴展類加載器,是 sun.misc.Launcher$ExtClassLoader類的實例。需要注意的是這里并沒有輸出引導(dǎo)類加載器,這是由于有些 JDK 的實現(xiàn)對于父類加載器是引導(dǎo)類加載器的情況,getParent()方法返回 null。

在了解了類加載器的樹狀組織結(jié)構(gòu)之后,下面介紹類加載器的代理模式。

類加載器的代理模式

類 加載器在嘗試自己去查找某個類的字節(jié)代碼并定義它時,會先代理給其父類加載器,由父類加載器先去嘗試加載這個類,依次類推。在介紹代理模式背后的動機之 前,首先需要說明一下 Java 虛擬機是如何判定兩個 Java 類是相同的。Java 虛擬機不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣。只有兩者都相同的情況,才認為兩個類是相同的。即便是同樣的字節(jié)代碼,被不同的類加 載器加載之后所得到的類,也是不同的。比如一個 Java 類 com.example.Sample,編譯之后生成了字節(jié)代碼文件 Sample.class。兩個不同的類加載器 ClassLoaderA和 ClassLoaderB分別讀取了這個 Sample.class文件,并定義出兩個 java.lang.Class類的實例來表示這個類。這兩個實例是不相同的。對于 Java 虛擬機來說,它們是不同的類。試圖對這兩個類的對象進行相互賦值,會拋出運行時異常 ClassCastException。下面通過示例來具體說明。代碼清單 3中給出了 Java 類 com.example.Sample。

清單 3. com.example.Sample 類
?package?com.example;?public?class?Sample?{?private?Sample?instance;?public?void?setSample(Object?instance)?{?this.instance?=?(Sample)?instance;?}?}

如 代碼清單 3所示,com.example.Sample類的方法 setSample接受一個 java.lang.Object類型的參數(shù),并且會把該參數(shù)強制轉(zhuǎn)換成 com.example.Sample類型。測試 Java 類是否相同的代碼如 代碼清單 4所示。

清單 4. 測試 Java 類是否相同
?public?void?testClassIdentity()?{?String?classDataRootPath?=?"C:\\workspace\\Classloader\\classData";?FileSystemClassLoader?fscl1?=?new?FileSystemClassLoader(classDataRootPath);?FileSystemClassLoader?fscl2?=?new?FileSystemClassLoader(classDataRootPath);?String?className?=?"com.example.Sample";? try?{?Class<?>?class1?=?fscl1.loadClass(className);?Object?obj1?=?class1.newInstance();?Class<?>?class2?=?fscl2.loadClass(className);?Object?obj2?=?class2.newInstance();?Method?setSampleMethod?=?class1.getMethod("setSample",?java.lang.Object.class);?setSampleMethod.invoke(obj1,?obj2);?}?catch?(Exception?e)?{?e.printStackTrace();?}?}

代碼清單 4中使用了類 FileSystemClassLoader的兩個不同實例來分別加載類 com.example.Sample,得到了兩個不同的 java.lang.Class的實例,接著通過 newInstance()方法分別生成了兩個類的對象 obj1和 obj2,最后通過 Java 的反射 API 在對象 obj1上調(diào)用方法 setSample,試圖把對象 obj2賦值給 obj1內(nèi)部的 instance對象。代碼清單 4的運行結(jié)果如 代碼清單 5所示。

清單 5. 測試 Java 類是否相同的運行結(jié)果
java.lang.reflect.InvocationTargetException? at?sun.reflect.NativeMethodAccessorImpl.invoke0(Native?Method)? at?sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)? at?sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at?java.lang.reflect.Method.invoke(Method.java:597)? at?classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26)? at?classloader.ClassIdentity.main(ClassIdentity.java:9)? Caused?by:?java.lang.ClassCastException:?com.example.Sample? cannot?be?cast?to?com.example.Sample? at?com.example.Sample.setSample(Sample.java:7)? ...?6?more

從 代碼清單 5給出的運行結(jié)果可以看到,運行時拋出了 java.lang.ClassCastException異常。雖然兩個對象 obj1和 obj2的類的名字相同,但是這兩個類是由不同的類加載器實例來加載的,因此不被 Java 虛擬機認為是相同的。

了解了這一點之后,就可以理解代理模式的設(shè)計動機了。代理模式是為了保證 Java 核心庫的類型安全。所有 Java 應(yīng)用都至少需要引用 java.lang.Object類,也就是說在運行的時候,java.lang.Object這個類需要被加載到 Java 虛擬機中。如果這個加載過程由 Java 應(yīng)用自己的類加載器來完成的話,很可能就存在多個版本的 java.lang.Object類,而且這些類之間是不兼容的。通過代理模式,對于 Java 核心庫的類的加載工作由引導(dǎo)類加載器來統(tǒng)一完成,保證了 Java 應(yīng)用所使用的都是同一個版本的 Java 核心庫的類,是互相兼容的。

不 同的類加載器為相同名稱的類創(chuàng)建了額外的名稱空間。相同名稱的類可以并存在 Java 虛擬機中,只需要用不同的類加載器來加載它們即可。不同類加載器加載的類之間是不兼容的,這就相當(dāng)于在 Java 虛擬機內(nèi)部創(chuàng)建了一個個相互隔離的 Java 類空間。這種技術(shù)在許多框架中都被用到,后面會詳細介紹。

下面具體介紹類加載器加載類的詳細過程。

加載類的過程

在前面介紹類加載器的代理模式的時候,提到過類加載器會首先代理給其它類加載器來嘗試加載某個類。這就意味著真正完成類的加載工作的類加載器和啟動這個加載過程的類加載器,有可能不是同一個。真正完成類的加載工作是通過調(diào)用 defineClass來實現(xiàn)的;而啟動類的加載過程是通過調(diào)用 loadClass來 實現(xiàn)的。前者稱為一個類的定義加載器(defining loader),后者稱為初始加載器(initiating loader)。在 Java 虛擬機判斷兩個類是否相同的時候,使用的是類的定義加載器。也就是說,哪個類加載器啟動類的加載過程并不重要,重要的是最終定義這個類的加載器。兩種類加 載器的關(guān)聯(lián)之處在于:一個類的定義加載器是它引用的其它類的初始加載器。如類 com.example.Outer引用了類 com.example.Inner,則由類 com.example.Outer的定義加載器負責(zé)啟動類 com.example.Inner的加載過程。

方法 loadClass()拋出的是 java.lang.ClassNotFoundException異常;方法 defineClass()拋出的是 java.lang.NoClassDefFoundError異常。

類加載器在成功加載某個類之后,會把得到的 java.lang.Class類的實例緩存起來。下次再請求加載該類的時候,類加載器會直接使用緩存的類的實例,而不會嘗試再次加載。也就是說,對于一個類加載器實例來說,相同全名的類只加載一次,即 loadClass方法不會被重復(fù)調(diào)用。

下面討論另外一種類加載器:線程上下文類加載器。

線程上下文類加載器

線程上下文類加載器(context class loader)是從 JDK 1.2 開始引入的。類 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設(shè)置線程的上下文類加載器。如果沒有通過 setContextClassLoader(ClassLoader cl)方法進行設(shè)置的話,線程將繼承其父線程的上下文類加載器。Java 應(yīng)用運行的初始線程的上下文類加載器是系統(tǒng)類加載器。在線程中運行的代碼可以通過此類加載器來加載類和資源。

前 面提到的類加載器的代理模式并不能解決 Java 應(yīng)用開發(fā)中會遇到的類加載器的全部問題。Java 提供了很多服務(wù)提供者接口(Service Provider Interface,SPI),允許第三方為這些接口提供實現(xiàn)。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的接口由 Java 核心庫來提供,如 JAXP 的 SPI 接口定義包含在 javax.xml.parsers包中。這些 SPI 的實現(xiàn)代碼很可能是作為 Java 應(yīng)用所依賴的 jar 包被包含進來,可以通過類路徑(CLASSPATH)來找到,如實現(xiàn)了 JAXP SPI 的 Apache Xerces所包含的 jar 包。SPI 接口中的代碼經(jīng)常需要加載具體的實現(xiàn)類。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory類中的 newInstance()方法用來生成一個新的 DocumentBuilderFactory的實例。這里的實例的真正的類是繼承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的實現(xiàn)所提供的。如在 Apache Xerces 中,實現(xiàn)的類是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。 而問題在于,SPI 的接口是 Java 核心庫的一部分,是由引導(dǎo)類加載器來加載的;SPI 實現(xiàn)的 Java 類一般是由系統(tǒng)類加載器來加載的。引導(dǎo)類加載器是無法找到 SPI 的實現(xiàn)類的,因為它只加載 Java 的核心庫。它也不能代理給系統(tǒng)類加載器,因為它是系統(tǒng)類加載器的祖先類加載器。也就是說,類加載器的代理模式無法解決這個問題。

線程上下文 類加載器正好解決了這個問題。如果不做任何的設(shè)置,Java 應(yīng)用的線程的上下文類加載器默認就是系統(tǒng)上下文類加載器。在 SPI 接口的代碼中使用線程上下文類加載器,就可以成功的加載到 SPI 實現(xiàn)的類。線程上下文類加載器在很多 SPI 的實現(xiàn)中都會用到。

下面介紹另外一種加載類的方法:Class.forName。

Class.forName

Class.forName是一個靜態(tài)方法,同樣可以用來加載類。該方法有兩種形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。第一種形式的參數(shù) name表示的是類的全名;initialize表示是否初始化類;loader表示加載時使用的類加載器。第二種形式則相當(dāng)于設(shè)置了參數(shù) initialize的值為 true,loader的值為當(dāng)前類的類加載器。Class.forName的一個很常見的用法是在加載數(shù)據(jù)庫驅(qū)動的時候。如 Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance()用來加載 Apache Derby 數(shù)據(jù)庫的驅(qū)動。

在介紹完類加載器相關(guān)的基本概念之后,下面介紹如何開發(fā)自己的類加載器。



轉(zhuǎn)載于:https://my.oschina.net/kakakaka/blog/337091

總結(jié)

以上是生活随笔為你收集整理的深入探讨 Java 类加载器(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美xxxx胸大 | 成人免费三级 | 日本h片在线观看 | 欧美高清视频一区二区三区 | 国产乱国产乱老熟 | 久久国产精品久久久久 | 自拍一区在线 | 日韩精品一区二区在线看 | 99热1| 日韩视频 中文字幕 | 国产精品美女久久久久 | 天天操天天干天天 | 欧美激精品 | jizz教师| av男人的天堂在线观看 | 国产伦精品一区二区三区妓女下载 | 美女在线不卡 | 免费的a级片 | 欧美美女视频 | 色综合天天干 | 欧美激情网站 | 欧美激情综合色综合啪啪五月 | 黄色三级网站在线观看 | 国产精品aⅴ | 色婷av| 亚洲v国产v欧美v久久久久久 | 狠狠爱网站| 呦呦在线视频 | 色噜噜日韩精品欧美一区二区 | 国产精品-色哟哟 | 国产欧美久久久久久 | 在线观看网站污 | 久久一级片 | 日韩欧美精品国产 | 国产视频一二 | av男人的天堂在线观看 | 都市激情校园春色亚洲 | 蜜桃9999| 伊人久久久久噜噜噜亚洲熟女综合 | 日韩www | 337p色噜噜| 国产乱论 | 色婷婷狠狠操 | 国产精品三级 | 未满十八18禁止免费无码网站 | 青青草国产在线播放 | 久草国产在线 | 国产区精品 | 欧美一区二区三区在线观看 | 国产一道本 | 国产999精品久久久久久 | 国产免费观看一区 | 亚洲精品视频一二三区 | 欧美xxxxhd| 插插插日日日 | a天堂在线观看 | 日本中文在线播放 | 国产精品99久久久久久人 | 亚洲精品一区二区三区新线路 | 少妇人妻好深好紧精品无码 | 国产又好看的毛片 | 国产精品高潮AV无码 | 精品久久999 | 日韩精品一区二区亚洲av性色 | 国产宾馆实践打屁股91 | 给我免费观看片在线电影的 | 狼人伊人久久 | jizz国产精品| 日本亲近相奷中文字幕 | 96超碰在线 | av二区在线 | 日韩成人av免费在线观看 | 九九热视频这里只有精品 | 91丨九色| 隔壁邻居是巨爆乳寡妇 | 免费黄色网址观看 | 黄色在线视频播放 | 久久精品牌麻豆国产大山 | 日本强好片久久久久久aaa | 国产精品一区二区三区四区五区 | 亚洲无套 | 风间由美在线视频 | 亚洲精品国产精品国自产网站按摩 | 91免费看片网站 | 黄色高潮 | 91重口味| 在线国产视频 | 99av在线| 91精品区 | 黄色网址视频 | 日日夜夜影院 | 无码人妻丰满熟妇奶水区码 | 国产鲁鲁视频在线观看免费 | 亚洲日本在线观看视频 | 成人免费毛片免费 | 中国极品少妇xxxx | 福利一区在线观看 | 女生毛片 | 久久狠狠高潮亚洲精品 |