JVM(三)類加載器

類的加載

類的加載是指將類的.class文件中二進制數(shù)據(jù)讀入到內(nèi)存中,然后將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在內(nèi)存中創(chuàng)建愛你一個java.lang.Class對象

規(guī)范并沒有說明Class對象應該存放在哪,HotSpot虛擬機將其放在方法區(qū)中,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結構

加載.class文件的方式

  • 從本地系統(tǒng)中直接加載
  • 從網(wǎng)絡下載.calss文件
  • 從zip,jar等歸檔文件中加載
  • 從專有數(shù)據(jù)庫中提取
  • 將Java源文件動態(tài)編譯為.class文件

servlet技術

類加載器

類加載器用來把類加載到Java虛擬機中,從JDK1.2版本開始,類的加載過程采用雙親委托機制,這種機制能保證Java平臺的安全性.

從源碼文檔中翻譯應該稱為父類委托模式

類加載器并不需要等到某個類被首次主動使用時再加載它

  • JVM規(guī)范允許類加載器在預料某個類將要被使用時就預先加載它,如果在預先加載的過程中遇到了.class文件缺失或者存在錯誤,類加載器必須在程序首次主動使用該類時才報告錯誤(LinkageError)
  • 如果一個類一直沒有被程序主動使用,那么累加載器就不會報告錯誤

JVM中的類加載器

根加載器(Bootstrap),

根加載器沒有父加載器,主要負責虛擬機的核心類庫,如java.lang.*等,java.lang.Object是由根類加載器加載的,根類加載器的實現(xiàn)依賴于底層操作系統(tǒng),屬于虛擬機實現(xiàn)第一部分,它并沒有繼承java.lang.ClassLoader類.
啟動類加載器是特定于平臺的機器指令,它負責開啟整個加載過程
啟動類加載器還會負責加載JRE正常運行所需的基本組件.其中包括java.util,java.lang包中的類

擴展類加載器(Extension)

擴展類加載器的父加載器是根加載器,從java.ext.dirs系統(tǒng)屬性指定的目錄中加載類庫,或者再jre\lib\ext子目錄下加載類庫,如果把用戶創(chuàng)建的JAR文件放在這個目錄下,會自動由擴展類加載器加載,擴展類加載器是純Java類,是ClassLoader的子類

注意一點的是,拓展類加載器加載的是jar包內(nèi)的class文件

系統(tǒng)(應用)類加載器(System/Application)

系統(tǒng)類加載器的父加載器為擴展類加載器,從環(huán)境變量classpath或者系統(tǒng)屬性java.class.path所制定的目錄加載類,它是用戶自定義的類加載器的默認父加載器,系統(tǒng)類加載器是純Java類,是ClassLoader的子類

用戶自定義的類加載器

除了虛擬機自帶的加載器外,用戶可以定制自己的類加載器.Java提供了抽象類ClassLoader.所有用戶自定義的加載器都應該繼承ClassLoader

AppClassLoader和ExtClassLoader都是Java類,所以需要類加載器進行加載,而這兩個類的類加載器就是bootstrapClassLoader

可以通過修改
System.getProperty(java.system.class.loader)對默認的SystemClassLoader進行修改

類加載器的層級關系

父親委托機制

在父親委托機制中,各個加載器按照父子關系形成樹形結構,除了根加載器之外,其余的類加載器有且只有一個父加載器.

父親委托機制

簡單描述,就是一個類加載器要加載一個類,并不是由自身進行直接加載,而是通過向上尋找父加載器,直到?jīng)]有父加載器的類加載器,然后再從上至下嘗試加載,直至找到一個可以正確加載的類加載器,一般情況下,系統(tǒng)類加載器就能加載普通的類.

并不是所有的類加載器都必須遵守雙親委托的機制,具體實現(xiàn)可以根據(jù)需要進行改造

代碼示例,查看類的加載器

public class Test08 {

    public static void main(String[] args) {
        try {
            Class<?> clzz = Class.forName("java.lang.String");
            //如果返回null,證明是由BootStrap加載器進行加載的
            System.out.println(clzz.getClassLoader());


            Class<?> customClass = Class.forName("com.r09er.jvm.classloader.Custom");
            System.out.println(customClass.getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Custom{

}

輸出

null
sun.misc.Launcher$AppClassLoader@18b4aac2

String的類加載器為null,證明String是由Bootstrap類加載器加載,因為根加載器是由C++實現(xiàn).所以會返回null.

Custom的類加載器是Launcher$AppClassLoader,這個類是不開源的.但是是默認的系統(tǒng)(應用)類加載器.

classLoader和初始化的時機

通過ClassLoader手動加載類,觀察是否會觸發(fā)類的初始化

public class Test12 {

    public static void main(String[] args) throws Exception {
        ClassLoader loader  = ClassLoader.getSystemClassLoader();
        Class<?> aClass = loader.loadClass("com.r09er.jvm.classloader.TestClassLoader");

        System.out.println(aClass);

        System.out.println("-------");

        aClass = Class.forName("com.r09er.jvm.classloader.TestClassLoader");

        System.out.println(aClass);

    }
}
class TestClassLoader{
    static {
        System.out.println("Test classloader");
    }
}

輸出

class com.r09er.jvm.classloader.TestClassLoader
-------
Test classloader
class com.r09er.jvm.classloader.TestClassLoader

結論

明顯可以看出,classLoader.load方法加載類,類并不會初始化,說明不是對類的主動使用,調(diào)用了Class.ForName才進行初始化

不同的類加載器與加載動作分析

打印類加載器,由于根加載器由C++編寫,所以就會返回null

public static void main(String[] args) {
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        System.out.println(loader);
        //向上遍歷父classLoader
        while (null != loader) {
            loader = loader.getParent();
            System.out.println(loader);
        }
    }

輸出結果

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@610455d6
null

獲取ClassLoader的途徑

  • 通過類對象獲取ClassLoader,clazz.getClassLoader()
  • 通過線程獲取上限文ClassLoader,Thread.currentThread().getContextLoader()
  • 獲得系統(tǒng)(應用)ClassLoader,ClassLoader.getSystemClassLoader()
  • 獲得調(diào)用者的ClassLoader,DriverManager.getClassLoader()

ClassLoader源碼分析

JavaDoc描述

類加載器是負責加載的對象,classLoader是抽象類.賦予類一個二進制名稱,一個類加載器應當嘗試定位生成數(shù)據(jù),這些數(shù)據(jù)構成類的定義.一種典型的策略是將二進制名稱轉(zhuǎn)換為文件名忆矛,然后從文件系統(tǒng)中讀取該名稱的字節(jié)碼文件

每一個對象都包含定義該的classLoader引用(reference)

數(shù)組對應的class對象并不是由類加載器創(chuàng)建的,而是由java虛擬機在需要時自動創(chuàng)建的.對于一個數(shù)組的類加載器,與這個數(shù)組元素的類加載器一致.如果數(shù)組是原生類型,那這個數(shù)組將沒有classLoader

String[],則這個數(shù)組的類加載器是String的類加載器,使用的是Bootstrap類加載器
int[] ,這種基本類型的數(shù)組,是沒有類加載器的.

應用實現(xiàn)classLoader的目的是為了拓展JVM動態(tài)加載類

ClassLoader使用了委托模型去尋找類的資源.ClassLoader的每一個實例都有會一個關聯(lián)的父ClassLoader,當需要尋找一個類的資源時,ClassLoader實例就會委托給父ClassLoader.虛擬機內(nèi)建的ClassLoader稱為BootstrapClassLoader,BootstrapClassLoader本身是沒有父ClassLoader的,但是可以作為其他ClassLoader的父加載器

支持并發(fā)加載的類加載器稱為并行類加載器,這種類加載器要求在類初始化期間通過ClassLoader.registerAsParallelCapable將自身注冊上.默認情況下就是并行的,而子類需要需要并行,則需要調(diào)用該方法

在委托機制并不是嚴格層次化的環(huán)境下,classLoader需要并行處理,否則類在加載過程中會導致死鎖,因為類加載過程中是持有鎖的

通常情況下,JVM會從本地的文件系統(tǒng)中加載類,這種加載與平臺相關.例如在UNIX系統(tǒng)中,jvm會從環(huán)境變量中CLASSPATH定義的目錄中加載類.

然而有些類并不是文件,例如網(wǎng)絡,或者由應用構建出來(動態(tài)代理),這種情況下,defineClass方法會將字節(jié)數(shù)組轉(zhuǎn)換為Class實例,可以通過Class.newInstance創(chuàng)建類真正的對象
由類加載器創(chuàng)建的對象的構造方法和方法,可能會引用其他的類,所以JVM會調(diào)用loadClass方法加載其他引用的類

二進制名稱BinaryNames,作為ClassLoader中方法的String參數(shù)提供的任何類名稱铸抑,都必須是Java語言規(guī)范所定義的二進制名稱遏暴。
例如

  • "java.lang.String",全限定類名
  • "javax.swing.JSpinner$DefaultEditor",內(nèi)部類
  • "java.security.KeyStoreBuilderFileBuilder$1",匿名內(nèi)部類
  • "java.net.URLClassLoader31"

自定義類加載器

步驟

  • 1.繼承CLassLoader
  • 2.重寫loadClass方法
  • 3.在loadClass方法中實現(xiàn)加載class字節(jié)碼的方法,返回byte[]
  • 4.調(diào)用super.defineClass(byte[])方法將Class對象返回給loadClass方法

源碼示例

public class Test16 extends ClassLoader {

    private String classLoaderName;

    private String path;

    private final String fileExtension = ".class";


    public Test16(String classLoaderName) {
        //將systemClassLoader作為當前加載器的父加載器
        super();
        this.classLoaderName = classLoaderName;
    }

    public Test16(ClassLoader parent, String classLoaderName) {
        //將自定義的ClassLoader作為當前加載器的父加載器
        super(parent);
        this.classLoaderName = classLoaderName;
    }


    public void setPath(String path) {
        this.path = path;
    }

    public static void main(String[] args) throws Exception {
        Test16 loader1 = new Test16("loader1");
        //設置絕對路徑,加載工程根目錄下的com.r09er.jvm.classloader.Test01.class
        loader1.setPath("/Users/cicinnus/Documents/sources/jvm-learning/");
        Class<?> aClass = loader1.loadClass("com.r09er.jvm.classloader.Test01");
        //打印加載的類
        System.out.println("loader1 load class" + aClass.hashCode());
        Object instance = aClass.newInstance();
        System.out.println("instance1: " + instance);


        Test16 loader2 = new Test16("loader2");
        //設置絕對路徑,加載工程根目錄下的Test01.class
        loader2.setPath("/Users/cicinnus/Documents/sources/jvm-learning/");
        Class<?> aClass2 = loader2.loadClass("com.r09er.jvm.classloader.Test01");
        System.out.println("loader2 load class" + aClass2.hashCode());
        Object instance2 = aClass2.newInstance();
        System.out.println("instance2 : " + instance2);

        //todo ****
        //1.重新編譯工程,確保默認的classPath目錄下有Test01.class的字節(jié)碼文件,然后運行main方法,觀察輸出
        //2.刪除默認classpath目錄下的Test01.class,運行main方法,觀察輸出

    }


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("invoke findClass");
        System.out.println("class loader name : " + this.classLoaderName);
        byte[] bytes = this.loadClassData(name);
        return super.defineClass(name, bytes, 0, bytes.length);
    }

    private byte[] loadClassData(String binaryName) {
        byte[] data = null;

        binaryName = binaryName.replace(".", "/");

        try (
                InputStream ins = new FileInputStream(new File(this.path + binaryName + this.fileExtension));
                ByteArrayOutputStream baos = new ByteArrayOutputStream();

        ) {
            int ch;
            while (-1 != (ch = ins.read())) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }

}

執(zhí)行兩次main方法后,會發(fā)現(xiàn)類加載器真正生效的邏輯,因為默認的父加載器其實是系統(tǒng)加載器(AppClassLoader),所以如果默認的classPath存在字節(jié)碼文件,則會由AppClassLoader正確加載類,如果classPath中沒有,則會向下使用自定義的類加載器加載類

如果構造函數(shù)傳入兩個不一樣的ClassLoaderName,會發(fā)現(xiàn)兩個class對象并不一致,是由于命名空間NameSpace的原因,因為兩個類加載器定義的名稱是不一樣的,如果改成相同的名稱,則兩個class對象一致

重寫的是findClass方法,在調(diào)用時候,使用的是classLoader的loadClass方法,這個方法內(nèi)部會調(diào)用findClass

還有一個重點,如果將class字節(jié)碼文件放在根目錄,則會拋出NoClassDefFoundError異常,因為binaryName不符合規(guī)范.

自定義類加載器加載類流程圖

類加載器重要方法詳解

findClass

實現(xiàn)自己的類加載器,最重要就是實現(xiàn)findClass,通過傳入binaryName,將二進制名稱加載成一個Class對象

defineClass

在實現(xiàn)findClass后,需要通過defineClass方法,將二進制數(shù)據(jù)交給defineClass方法轉(zhuǎn)換成一個Class實例,
defineClass內(nèi)部會做一些保護和檢驗工作.

雙親委派機制解析

通過loadClass方法加載類,會有如下默認加載順序

  • 1.調(diào)用findLoadedClass方法檢查class是否被加載
  • 2.調(diào)用父加載器的loadClass方法,如果父加載器為null,則會調(diào)用JVM內(nèi)建的類加載器.
  • 3.調(diào)用findClass方法找到類

在默認的loadClass方法中,類加載是同步

雙親委派機制優(yōu)點

  • 1.可以確保Java核心類庫的類型安全,如果這個加載過程由Java應用自己的類加載器完成,很可能會在JVM中存在多個版本的同一個類(包名,類名一致),

命名空間發(fā)揮的作用

  • 2.可以確保Java核心類庫提供的類不會被自定義的類替代

因為優(yōu)先加載的是類庫中的class,會忽略掉自定義的類

  • 3.不同的類加載器可以為相同名稱(binaryName)的類創(chuàng)建額外的命名空間,相同名稱的類可以并存在JVM中.

類的卸載

當類被加載,連接,初始化之后,它的生命周期就開始了.當代表類的Class對象不再被引用,即不可觸及時,Class對象就會結束生命周期,類在元空間內(nèi)的數(shù)據(jù)也會被卸載,從而結束類的生命周期.

一個類何時結束生命周期,取決于代表它的Class對象何時結束生命周期

由Java虛擬機自帶的類加載器所加載的類,在虛擬機的生命周期中,始終不會被卸載.

用戶自定義的類加載器所加載的類是可以被卸載的

類加載器加載的類路徑

BootstrapClassLoader加載的路徑

  • System.getProperty("sun.boot.class.path")

ExtClassLoader

  • System.getProperty("java.ext.dirs")

AppClassLoader

  • System.getProperty("java.class.path")

三個路徑和JDK版本,操作系統(tǒng)都有關系

如果將編譯好的class字節(jié)碼文件放到根加載器的加載路徑上,可以成功由BootstrapClassLoader加載

類加載器命名空間

  • 每個類加載器都有自己的命名空間,命名空間由該加載器及所有父加載器所加載的類組成

即子加載器能訪問父加載器加載的類,而父加載器不能訪問子加載器加載的類.(類似于繼承的概念)

  • 在同一個命名空間中,不會出現(xiàn)類的完整名字相同的兩個類

一個Java類是由該類的全限定名稱+用于加載該類的定義類加載器(defining loader)共同決定.

ClassLoader.getSystemClassLoader源碼

返回用于委托的系統(tǒng)類加載器.是自定義類加載器的父加載器,通常情況下類會被系統(tǒng)類加載器加載.
該方法在程序運很早的時間就會被創(chuàng)建,并且會將系統(tǒng)類加載器設為調(diào)用線程的上下文類加載器(context class loader)

Launcher構造主要邏輯

1.初始化ExtClassLoader
2.初始化AppClassLoader,將初始化好的ExtClassLoader設置為AppClassLoader的父加載器
3.將AppClassLoader設置為當前線程的上下文類加載器

SystemClassLoaderAction邏輯

1.判斷System.getProperty("java.system.class.loader")是否有設置系統(tǒng)類加載器
2.如果為空,直接返回AppClassLoader
3.如果不為空,通過反射創(chuàng)建classLoader,其中必須提供一個函數(shù)簽名為ClassLoader的構造
4.將反射創(chuàng)建的自定義類加載器設置為上限為加載器.
5.返回創(chuàng)建好的類加載器

Class.ForName(name,initialize,classloader)解析

  • name,需要構造的類全限定名稱(binaryName)

不能用于原生類型或者void類型
如果表示的是數(shù)組,則會加載數(shù)組中的元素class對象,但是不進行初始化

  • initialize,類是否需要初始化
  • classloader,加載此類的類加載器

線程上下文加載器(ContextClassLoader)實現(xiàn)與分析

CurrentClassLoader(當前類加載器)

  • 每一個類都會嘗試使用自己的ClassLoader去加載當前類引用的其他類

如果ClassA引用了ClassY,那么ClassA的類加載器會去加載ClassY,前提是ClassY未被加載

線程類加載器從JDK1.2開始引入,Thread類中的getContextClassLoadersetContextClassLoader分別用來獲取和設置上下文加載器.如果沒有手動進行設置,那么線程會繼承其父線程的上下文加載器.
java應用運行時的初始線程的上下文類加載器是系統(tǒng)類加載器(AppClassLoader),在線程中運行的類可以通過這個類加載器加載類與資源

由JDBC引出的問題

回顧一下JDBC操作

Class.forName("com.mysql.driver.Driver");
Connection conn = Driver.connect();
Statement stae = conn.getStatement();

Driver,Connection,Statement都是由JDK提供的標準,而實現(xiàn)是由具體的DB廠商提供.
根據(jù)類加載的機制,JDK的rt包會被BootstrapClassLoader加載,而自定義的類會被AppClassLoader加載,同時因為命名空間的原因,父加載器是無法訪問子加載器加載的類的.所以父加載器會導致這個問題.

上下文加載器就是為了解決這種問題所存在的

父ClassLaoder可以使用當前線程Thread.currentThread().getContextClassLoader()加載的類,
這就改變了父ClassLoader不能使用子ClassLoader或是其他沒有直接父子關系的ClassLoader無法訪問對方加載的class問題.

即改變了父親委托模型

線程上下文加載器一般使用

使用步驟(獲取 - 使用 - 還原)

  1. Thread.currentThread().getContextClassLoader()
  2. Thread.currentThread().setContextClassLoader(targetClassLoader)
    doSomentthing();
    3.Thread.currentThread().setContextClassLoader(originClassLoader);

ContextClassLoader的作用就是破壞Java的類加載委托機制

ServiceLoader

ServiceLoader是一個簡單的服務提供者加載設施

加載基于JDK規(guī)范接口實現(xiàn)的具體實現(xiàn)類
實現(xiàn)類需要提供無參構造,用于反射構造出示例對象

服務提供者將配置文件放到資源目錄的META-INF/services目錄下,告訴JDK在此目錄的文件內(nèi)配置了需要加載的類,其中文件名稱是需要加載的接口全限定名稱,文件內(nèi)容是一個或多個實現(xiàn)的類全限定名稱.

總結

在雙親委托模型下,類加載時由下至上的.但是對于SPI機制來說,有些接口是由Java核心庫提供的,根據(jù)類加載的機制,JDK的rt包會被BootstrapClassLoader加載,而自定義的類會被AppClassLoader加載.這樣傳統(tǒng)的雙親委托模型就不能滿足SPI的情況,就可以通過線程上下文加載器來實現(xiàn)對于接口實現(xiàn)類的加載.

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侄刽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子朋凉,更是在濱河造成了極大的恐慌州丹,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杂彭,死亡現(xiàn)場離奇詭異墓毒,居然都是意外死亡,警方通過查閱死者的電腦和手機亲怠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門所计,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人团秽,你說我怎么就攤上這事主胧“仁祝” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵讥裤,是天一觀的道長放棒。 經(jīng)常有香客問我,道長己英,這世上最難降的妖魔是什么间螟? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮损肛,結果婚禮上厢破,老公的妹妹穿的比我還像新娘。我一直安慰自己治拿,他們只是感情好摩泪,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劫谅,像睡著了一般见坑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捏检,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天荞驴,我揣著相機與錄音,去河邊找鬼贯城。 笑死熊楼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的能犯。 我是一名探鬼主播鲫骗,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼踩晶!你這毒婦竟也來了执泰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤合瓢,失蹤者是張志新(化名)和其女友劉穎坦胶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晴楔,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡顿苇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了税弃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纪岁。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖则果,靈堂內(nèi)的尸體忽然破棺而出幔翰,到底是詐尸還是另有隱情漩氨,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布遗增,位于F島的核電站叫惊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏做修。R本人自食惡果不足惜霍狰,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饰及。 院中可真熱鬧蔗坯,春花似錦、人聲如沸燎含。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屏箍。三九已至绘梦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赴魁,已是汗流浹背谚咬。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尚粘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓敲长,卻偏偏與公主長得像郎嫁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子祈噪,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

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

  • JAVA虛擬機(JVM)系列:JAVA虛擬機(JVM)一:了解JAVA體系結構JAVA虛擬機(JVM)二:JVM工...
    IT前沿技術分享閱讀 1,232評論 0 2
  • 本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發(fā)布 ClassLoader翻譯過來就是類加載器泽铛,普...
    尼爾君閱讀 661評論 1 0
  • 轉(zhuǎn)發(fā):本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發(fā)布 ClassLoader翻譯過來就是類加載...
    尼爾君閱讀 533評論 0 1
  • 1、classLoader 類加載器辑鲤,將class文件加載到JVM虛擬機內(nèi)存中盔腔,使得程序可以運行。通常情況下月褥,JV...
    helloWorld_1118閱讀 2,213評論 0 2
  • ClassLoader翻譯過來就是類加載器弛随,普通的java開發(fā)者其實用到的不多,但對于某些框架開發(fā)者來說卻非常常見...
    時待吾閱讀 1,076評論 0 1