Java類加載器

java類加載器基本概念

類加載器是用來把java類加載到虛擬機中的胰柑。一般來說java虛擬機使用java類的步驟如下:.java文件經(jīng)過編譯器編譯后生成.class文件让腹。類加載器負責(zé)讀取這些字節(jié)碼文件捣鲸,并轉(zhuǎn)化成一個java.lang.Class類的實例。每一個這樣的實例都表示一個java類剥悟。通過這個實例的newInstance()就可以創(chuàng)建出該類的一個對象。

基本上所有的類加載器都是java.lang.ClassLoader的一個實例。

java.lang.ClassLoader介紹

方法 說明
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 類亩进。

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()來獲取它蜡歹。

除了引導(dǎo)類加載器,其他的類加載器都必須有一個父類加載器涕烧≡露可以通過getParent()來獲取父類加載器。系統(tǒng)類加載器的父類加載器是擴展類加載器议纯,而擴展類加載器的父類加載器是引導(dǎo)類加載器父款;對于開發(fā)人員編寫的類加載器來說,其父類加載器是加載此類加載器 Java 類的類加載器瞻凤。

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

每個java類都維護類一個指向定義它(調(diào)用defineClass來創(chuàng)建該類實例的classloader)的類加載器的引用肝集,可以通過getClassLoader()來獲取。

類加載器的代理模式

類加載器在嘗試查找一個類的字節(jié)碼文件并定義它時蛛壳,后先代理給父類加載器杏瞻,有父類加載器先去嘗試加載這個類,以此類推衙荐。在介紹這個代理模式之前伐憾,先說明一下java虛擬機時如何判定兩個java類是否相同的。jvm不僅要判斷兩類的類名是否相同赫模,還要比較加載這兩個類的加載器是否一樣。只有當(dāng)兩種都相同時蒸矛,才認(rèn)為兩個類時相同的瀑罗。即便是相同的字節(jié)碼,被不同的類加載器加載生成后所得到的類雏掠,也是不同的斩祭。下面來看個例子:

自定義的類加載器

public class FileSystemClassLoader extends ClassLoader {

    private String rootDir;

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] getClassData(String className) {
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String classNameToPath(String className) {
        return rootDir + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }
}

Sample類

package com.example; 

public class Sample {
    private Sample instance;

    public void setSample(Object instance) {
        this.instance = (Sample) instance;
    }
}

測試方法

public void testClassIdentity() {
    String classDataRootPath = "";//字節(jié)碼文件路徑
    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();
    }
}

運行程序會報java.lang.ClassCastException,說明這個兩個類不相同乡话。

了解了這一點之后摧玫,就可以理解代理模式的設(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 類空間。

加載類的過程

在前面介紹類加載器的代理模式的時候壁拉,提到過類加載器會首先代理給其它類加載器來嘗試加載某個類谬俄。這就意味著真正完成類的加載工作的類加載器和啟動這個加載過程的類加載器,有可能不是同一個弃理。真正完成類的加載工作是通過調(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)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末统诺,一起剝皮案震驚了整個濱河市歪脏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粮呢,老刑警劉巖婿失,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钞艇,死亡現(xiàn)場離奇詭異,居然都是意外死亡豪硅,警方通過查閱死者的電腦和手機哩照,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懒浮,“玉大人飘弧,你說我怎么就攤上這事⊙庵” “怎么了次伶?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稽穆。 經(jīng)常有香客問我冠王,道長,這世上最難降的妖魔是什么舌镶? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任柱彻,我火速辦了婚禮,結(jié)果婚禮上餐胀,老公的妹妹穿的比我還像新娘哟楷。我一直安慰自己,他們只是感情好否灾,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布卖擅。 她就那樣靜靜地躺著,像睡著了一般墨技。 火紅的嫁衣襯著肌膚如雪磨镶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天健提,我揣著相機與錄音,去河邊找鬼伟叛。 笑死私痹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的统刮。 我是一名探鬼主播紊遵,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侥蒙!你這毒婦竟也來了暗膜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鞭衩,失蹤者是張志新(化名)和其女友劉穎学搜,沒想到半個月后娃善,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡瑞佩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年聚磺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炬丸。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘫寝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出稠炬,到底是詐尸還是另有隱情焕阿,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布首启,位于F島的核電站暮屡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闽坡。R本人自食惡果不足惜栽惶,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疾嗅。 院中可真熱鬧外厂,春花似錦、人聲如沸代承。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽论悴。三九已至掖棉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膀估,已是汗流浹背幔亥。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留察纯,地道東北人帕棉。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像饼记,于是被迫代替她去往敵國和親香伴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 作者:成 富, 軟件工程師, IBM 中國軟件開發(fā)中心 類加載器(class loader)是 Java?中的一個...
    Android技術(shù)研究閱讀 3,905評論 0 74
  • 類加載器是 Java 語言的一個創(chuàng)新具则,也是 Java 語言流行的重要原因之一即纲。它使得 Java 類可以被動態(tài)加載到...
    CHSmile閱讀 1,598評論 0 12
  • 類加載的時機 類從被加載到虛擬機內(nèi)存中開始,到卸載出內(nèi)存為止博肋,它的整個生命周期包括:加載(Loading)低斋、驗證(...
    FX_SKY閱讀 2,308評論 0 5
  • 大部分人平時不會直接接觸到ClassLoader蜂厅,但ClassLoader作為Java的一個重要的核心特性卻又和平...
    簡xiaoyao閱讀 1,692評論 0 10
  • 我喜歡文字,因為文字它是單純美麗的拔稳,它會有魔法葛峻。 我一直都這樣認(rèn)為,文字是人類心底最深卻也最真城的情感巴比。每個...
    青年寫作閱讀 365評論 0 2