類加載機制--雙親委派機制

類加載機制–雙親委派機制


說雙親委派機制之前,先來談談類加載器

類加載器

在這裏插入圖片描述
看上述圖之後,就知道類加載器有四類

1、啓動類加載器(Bootstrap ClassLoader)

這個類將器負責將存放在<JAVA_HOME>\lib目錄中的,並且是虛擬機識別的(僅按照文件名識別,如rt.jar,名字不符合的類庫即使放在lib目錄中也不會被加載)類庫加載到虛擬機內存中。啓動類加載器無法被Java程序直接引用,用戶在編寫自定義類加載器時,如果需要把加載請求委派給引導類加載器,那直接使用null指定父類加載器

2、擴展類加載器(Extension ClassLoader)

這個加載器由sun.misc.Launcher $ExtClassLoader實現,它負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器

3、應用類加載器(Application ClassLoader)

這個類加載器由sun.misc.Launcher $App-ClassLoader實現。由於這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱它爲系統類加載器。它負責加載用戶類路徑(ClassPath)上所指定的類庫,開發者可以直接使用這個類加載器如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。

4、自定義加載器

自己定義的類加載器,用於自己加載自己所定義的類,一般不寫參數,默認爲父類爲應用類加載器,如果參數爲null,則父類爲啓動類加載器。一般繼承ClassLoader然後重寫構造方法。
在這裏插入圖片描述

雙親委派機制

雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試自己去加載。

通俗來說:就是想要加載一個類,都會交給自己父類進行處理,如果父類處理不了,子類纔會進行加載,因爲每個父類都有自己的加載範圍。

雙親委派模型對於保證Java程序的穩定運作很重要,但它的實現卻非常簡單,實現雙親委派的代碼都集中在java.lang.ClassLoader的loadClass () 方法之中
邏輯清晰易懂:先檢查是否已經被加載過,若沒有加載則調用父加載器的loadClass()方法,若父加載器爲空則默認使用啓動類加載器作爲父加載器。如果父類加載失敗,拋出ClassNotFoundException異常後,再調用自己的findClass()方法進行加載。

看下實現源碼

private final ClassLoader parent;

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先檢查是否已經加載了該類
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 父類加載器加載,如果父類加載器爲空,使用最頂級的Bootstrap類加載器來加載
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }


            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                // 如果以上步驟還沒找到類,就按照自己定義的類加載器中重寫的findClass方法加載
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

注意:雙親委派的具體邏輯就實現在這個方法之中,JDK1.2之後已不提倡用戶再去覆蓋loadClass()方法,而應當把自己的類加載邏輯寫到findClass()方法中,在loadClass()方法的邏輯裏如果父類加載失敗,則會調用自己的findClass()方法來完成加載,這樣就可以保證新寫出來的類加載器是符合雙親委派規則的。

重寫loadClass():不遵循雙親委派機制,按照自己的方式加載
重寫findClass():遵循雙親委派機制,只有父類找不到時,才按照自己的方式加載

如果自定義的類加載器遵循雙親委派機制,寫了一個Java.lang.String,想去加載自己定義的這個String類,然後就會拋出異常,就會說不能用java.lang包名,如果非要加載自己定義的(和java內庫中同名的類),就要手動重寫loadClass(),就不會遵循雙親委派機制,按照自己的方式加載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章