Tip-Java的雙親委派機制

類加載的雙親委派模型

       雙親委派模型要求除了頂層的啓動類加載器外,其餘的類加載器都應當有本身的父類加載器。這裏類加載器之間的父子關係通常不會以繼承關係來實現,而是都使用組合關係來複用父加載器的代碼javascript

工做過程:

       若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該傳遞到頂層的啓動類加載器中,只有當父類加載器反饋本身沒法完成這個請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試本身去加載java

好處:

        Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係。例如類Object,它放在rt.jar中,不管哪個類加載器要加載這個類,最終都是委派給啓動類加載器進行加載,所以Object類在程序的各類類加載器環境中都是同一個類。
判斷兩個類是否相同是經過classloader.class這種方式進行的,因此哪怕是同一個class文件若是被兩個classloader加載,那麼他們也是不一樣的類bootstrap

類加載器按照層次,從頂層到底層,分爲如下三種:

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

        這個類加載器負責將存放在JAVA_HOME/lib下的,或者被-Xbootclasspath參數所指定的路徑中的,而且是虛擬機識別的類庫加載到虛擬機內存中。啓動類加載器沒法被Java程序直接引用。app

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

        這個加載器負責加載JAVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的全部類庫,開發者能夠直接使用擴展類加載器ui

3.應用程序類加載器(Application ClassLoader)

        這個加載器是ClassLoader中getSystemClassLoader()方法的返回值,因此通常也稱它爲系統類加載器。它負責加載用戶類路徑(Classpath)上所指定的類庫,可直接使用這個加載器,若是應用程序沒有自定義本身的類加載器,通常狀況下這個就是程序中默認的類加載器。this

       關於這部分的問題,能夠直接閱讀sun.misc.Launcher的源碼,或者看末尾連接給出的文章。url

實現本身的加載器

       只須要繼承ClassLoader,並覆蓋findClass方法。在調用loadClass方法時,會先根據委派模型在父加載器中加載,若是加載失敗,則會調用本身的findClass方法來完成加載。spa

類加載器如何識別

       經過findLoadedClass來識別是否已經加載某個類,這個方法是findLoadedClass0的一個包裝類,而findLoadedClass0是一個native方法.net

顯式加載類

在代碼中顯式加載某個類,有三種方法:code

1.    this.getClass().getClassLoader().loadClass()
2.    Class.forName()
3.    MyClassLoader.findClass()複製代碼

在大多數狀況下,並不須要本身實現一個類加載器,而是直接採用URLClassLoader來加載外部Class或者jar文件。JDK中的AppClassloader也是繼承的URLClassLoader:

public class Test{
    public static void main(String[] args) {
        URL url = null;

        try {
            url = new File("/tmp/Test.class").toURI().toURL();

                ClassLoader loader = new URLClassLoader(urls);
                classLoaderList.add(loader);
                loader.loadClass("com.paddx.test.memory.Test");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}複製代碼

ClassLoader.loadClass()的加載步驟:

1.    調用 findLoadedClass(String) 來檢查是否已經加載類。
2.    在父類加載器上調用 loadClass 方法。若是父類加載器爲 null,則使用虛擬機的內置類加載器。
3.    調用 findClass(String) 方法查找類。複製代碼

       ClassLoader是一個抽象類。Java 默認實現是AppClassLoader。 從源碼能夠看出來findClass和loadClass的不一樣之處:findClass只是loadClass的其中一步,若是父加載器和根加載器都沒有找到這個類,就會調用findClass方法。若是繼承了findClass方法,那麼雙親委派機制就不會被破壞。這實際上是一個模板方法模式。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    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.
                    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;
        }
    }複製代碼

因爲抽象類的默認findClass實現是:

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }複製代碼

       因此子類必須實現這個方法。

下面這個文章講的挺好:
blog.csdn.net/jiangwei091…

相關文章
相關標籤/搜索