<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Live a simple life

    沉默(zhu_xing@live.cn)
    隨筆 - 48, 文章 - 0, 評論 - 132, 引用 - 0
    數據加載中……

    【原創】Java類加載原理解析

     

    Java類加載原理解析                                        

    1       基本信息

    摘要:

    每個java開發人員對java.lang.ClassNotFoundExcetpion這個異常肯定都不陌生,這背后就涉及到了java技術體系中的類加載。Java的類加載機制是java技術體系中比較核心的部分,雖然和大部分開發人員直接打交道不多,但是對其背后的機理有一定理解有助于排查程序中出現的類加載失敗等技術問題,對理解java虛擬機的連接模型和java語言的動態性都有很大幫助。

    由于關于java類加載的內容較多,所以打算分三篇文章簡述一下:

    第一篇:java類加載原理解析

    第二篇:插件環境下類加載原理解析

    第三篇:線程上下文類加載器

    分類開發技術->J2EE

    標簽:Java類加載 類加載器 雙親委派機制 自定義類加載器

    作者:朱興 創建于2007-6-22          MSNzhu_xing@live.cn 

    2       Java虛擬機類加載器結構簡述

    2.1    JVM三種預定義類型類加載器

    我們首先看一下JVM預定義的三種類型類加載器,當一個 JVM 啟動的時候,Java 缺省開始使用如下三種類型類裝入器:

    啟動(Bootstrap)類加載器:引導類裝入器是用本地代碼實現的類裝入器,它負責將 <Java_Runtime_Home>/lib 下面的類庫加載到內存中。由于引導類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啟動類加載器的引用,所以不允許直接通過引用進行操作。

    標準擴展(Extension)類加載器:擴展類加載器是由 Sun ExtClassLoadersun.misc.Launcher$ExtClassLoader 實現的。它負責將 < Java_Runtime_Home >/lib/ext 或者由系統變量 java.ext.dir 指定位置中的類庫加載到內存中。開發者可以直接使用標準擴展類加載器。

    系統(System)類加載器:系統類加載器是由 Sun AppClassLoadersun.misc.Launcher$AppClassLoader)實現的。它負責將系統類路徑(CLASSPATH)中指定的類庫加載到內存中。開發者可以直接使用系統類加載器。

    除了以上列舉的三種類加載器,還有一種比較特殊的類型就是線程上下文類加載器,這個將在后面單獨介紹。

    2.2    類加載雙親委派機制介紹和分析

           在這里,需要著重說明的是,JVM在加載類時默認采用的是雙親委派機制。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。關于虛擬機默認的雙親委派機制,我們可以從系統類加載器和標準擴展類加載器為例作簡單分析。

                     
                     圖一 標準擴展類加載器繼承層次圖

                       
            圖二 系統類加載器繼承層次圖

           通過圖一和圖二我們可以看出,類加載器均是繼承自java.lang.ClassLoader抽象類。我們下面我們就看簡要介紹一下java.lang.ClassLoader中幾個最重要的方法:

    //加載指定名稱(包括包名)的二進制類型,供用戶調用的接口

    public Class<?> loadClass(String name) throws ClassNotFoundException{//…}

    //加載指定名稱(包括包名)的二進制類型,同時指定是否解析(但是,這里的resolve參數不一定真正能達到解析的效果~_~),供繼承用

    protectedsynchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{//…}

    //findClass方法一般被loadClass方法調用去加載指定名稱類,供繼承用

    protected Class<?> findClass(String name) throws ClassNotFoundException {//…}

    //定義類型,一般在findClass方法中讀取到對應字節碼后調用,可以看出不可繼承(說明:JVM已經實現了對應的具體功能,解析對應的字節碼,產生對應的內部數據結構放置到方法區,所以無需覆寫,直接調用就可以了)

    protected final Class<?> defineClass(String name, byte[] b, int off, int len)

    throws ClassFormatError{//…}

           通過進一步分析標準擴展類加載器(sun.misc.Launcher$ExtClassLoader)和系統類加載器(sun.misc.Launcher$AppClassLoader)的代碼以及其公共父類(java.net.URLClassLoaderjava.security.SecureClassLoader)的代碼可以看出,都沒有覆寫java.lang.ClassLoader中默認的加載委派規則---loadClass)方法。既然這樣,我們就可以通過分析java.lang.ClassLoader中的loadClassString name)方法的代碼就可以分析出虛擬機默認采用的雙親委派機制到底是什么模樣:

    public Class<?> loadClass(String name)throws ClassNotFoundException {

            return loadClass(name, false);

    }

    protectedsynchronized Class<?> loadClass(String name, boolean resolve)

                throws ClassNotFoundException {

            // 首先判斷該類型是否已經被加載

            Class c = findLoadedClass(name);

            if (c == null) {

                //如果沒有被加載,就委托給父類加載或者委派給啟動類加載器加載

                try {

                    if (parent != null) {

    //如果存在父類加載器,就委派給父類加載器加載

                        c = parent.loadClass(name, false);

                    } else {

    //如果不存在父類加載器,就檢查是否是由啟動類加載器加載的類,通過調用本地方法native Class findBootstrapClass(String name)

                        c = findBootstrapClass0(name);

                    }

                } catch (ClassNotFoundException e) {

            // 如果父類加載器和啟動類加載器都不能完成加載任務,才調用自身的加載功能

                    c = findClass(name);

                }

            }

            if (resolve) {

                resolveClass(c);

            }

            return c;

        }

        通過上面的代碼分析,我們可以對JVM采用的雙親委派類加載機制有了更感性的認識,下面我們就接著分析一下啟動類加載器、標準擴展類加載器和系統類加載器三者之間的關系。可能大家已經從各種資料上面看到了如下類似的一幅圖片:


                        
                                 圖三 類加載器默認委派關系圖

    上面圖片給人的直觀印象是系統類加載器的父類加載器是標準擴展類加載器,標準擴展類加載器的父類加載器是啟動類加載器,下面我們就用代碼具體測試一下:

    示例代碼:

    public static void main(String[] args) {
       
    try {
         System.out.println(ClassLoader.getSystemClassLoader());
         System.out.println(ClassLoader.getSystemClassLoader().getParent();
         System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
       } 
    catch (Exception e) {
           e.printStackTrace();
       }
    }


    說明:通過java.lang.ClassLoader.getSystemClassLoader()可以直接獲取到系統類加載器。

    代碼輸出如下:

    sun.misc.Launcher$AppClassLoader@197d257

    sun.misc.Launcher$ExtClassLoader@7259da

    null

        通過以上的代碼輸出,我們可以判定系統類加載器的父加載器是標準擴展類加載器,但是我們試圖獲取標準擴展類加載器的父類加載器時確得到了null,就是說標準擴展類加載器本身強制設定父類加載器為null。我們還是借助于代碼分析一下:

         我們首先看一下java.lang.ClassLoader抽象類中默認實現的兩個構造函數:

        protected ClassLoader() {

            SecurityManager security = System.getSecurityManager();

            if (security != null) {

                security.checkCreateClassLoader();

            }

            //默認將父類加載器設置為系統類加載器,getSystemClassLoader()獲取系統類加載器

            this.parent = getSystemClassLoader();

            initialized = true;

        }

        protected ClassLoader(ClassLoader parent) {

            SecurityManager security = System.getSecurityManager();

            if (security != null) {

                security.checkCreateClassLoader();

            }

            //強制設置父類加載器

            this.parent = parent;

            initialized = true;

        }

        我們再看一下ClassLoader抽象類中parent成員的聲明:

           // The parent class loader for delegation

    private ClassLoader parent;

    聲明為私有變量的同時并沒有對外提供可供派生類訪問的public或者protected設置器接口(對應的setter方法),結合前面的測試代碼的輸出,我們可以推斷出:

    1.              系統類加載器(AppClassLoader)調用ClassLoader(ClassLoader parent)構造函數將父類加載器設置為標準擴展類加載器(ExtClassLoader)。(因為如果不強制設置,默認會通過調用getSystemClassLoader()方法獲取并設置成系統類加載器,這顯然和測試輸出結果不符。)

    2.               擴展類加載器(ExtClassLoader調用ClassLoader(ClassLoader parent)構造函數將父類加載器設置為null。(因為如果不強制設置,默認會通過調用getSystemClassLoader()方法獲取并設置成系統類加載器,這顯然和測試輸出結果不符。)

         現在我們可能會有這樣的疑問:擴展類加載器(ExtClassLoader)的父類加載器被強制設置為null了,那么擴展類加載器為什么還能將加載任務委派給啟動類加載器呢?

                  

             圖四 標準擴展類加載器和系統類加載器成員大綱視圖

                      
               
               圖五
    擴展類加載器和系統類加載器公共父類成員大綱視圖

        通過圖四和圖五可以看出,標準擴展類加載器和系統類加載器及其父類(java.net.URLClassLoaderjava.security.SecureClassLoader都沒有覆寫java.lang.ClassLoader中默認的加載委派規則---loadClass)方法。有關java.lang.ClassLoader中默認的加載委派規則前面已經分析過,如果父加載器為null,則會調用本地方法進行啟動類加載嘗試。所以,圖三中,啟動類加載器、標準擴展類加載器和系統類加載器之間的委派關系事實上是仍就成立的。(在后面的用戶自定義類加載器部分,還會做更深入的分析)。

    2.3    類加載雙親委派示例

    以上已經簡要介紹了虛擬機默認使用的啟動類加載器、標準擴展類加載器和系統類加載器,并以三者為例結合JDK代碼對JVM默認使用的雙親委派類加載機制做了分析。下面我們就來看一個綜合的例子。首先在eclipse中建立一個簡單的java應用工程,然后寫一個簡單的JavaBean如下:

    package classloader.test.bean;

        publicclass TestBean {

            public TestBean() {}

    }

    在現有當前工程中另外建立一測試類(ClassLoaderTest.java)內容如下:

    測試一:

    publicclass ClassLoaderTest {

        publicstaticvoid main(String[] args) {

            try {

                //查看當前系統類路徑中包含的路徑條目

                System.out.println(System.getProperty("java.class.path"));

    //調用加載當前類的類加載器(這里即為系統類加載器)加載TestBean

    Class typeLoaded = Class.forName("classloader.test.bean.TestBean");

    //查看被加載的TestBean類型是被那個類加載器加載的

                System.out.println(typeLoaded.getClassLoader());

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

    對應的輸出如下:

    D:"DEMO"dev"Study"ClassLoaderTest"bin

    sun.misc.Launcher$AppClassLoader@197d257

    (說明:當前類路徑默認的含有的一個條目就是工程的輸出目錄)

    測試二:

    將當前工程輸出目錄下的…/classloader/test/bean/TestBean.class打包進test.jar剪貼< Java_Runtime_Home >/lib/ext目錄下(現在工程輸出目錄下和JRE擴展目錄下都有待加載類型的class文件)。再運行測試一測試代碼,結果如下:

    D:"DEMO"dev"Study"ClassLoaderTest"bin

    sun.misc.Launcher$ExtClassLoader@7259da

    對比測試一和測試二,我們明顯可以驗證前面說的雙親委派機制,系統類加載器在接到加載classloader.test.bean.TestBean類型的請求時,首先將請求委派給父類加載器(標準擴展類加載器),標準擴展類加載器搶先完成了加載請求。

        測試三:

    test.jar拷貝一份到< Java_Runtime_Home >/lib下,運行測試代碼,輸出如下:

    D:"DEMO"dev"Study"ClassLoaderTest"bin

    sun.misc.Launcher$ExtClassLoader@7259da

       測試三和測試二輸出結果一致。那就是說,放置到< Java_Runtime_Home >/lib目錄下的TestBean對應的class字節碼并沒有被加載,這其實和前面講的雙親委派機制并不矛盾。虛擬機出于安全等因素考慮,不會加載< Java_Runtime_Home >/lib存在的陌生類開發者通過將要加載的非JDK自身的類放置到此目錄下期待啟動類加載器加載是不可能的。做個進一步驗證,刪除< Java_Runtime_Home >/lib/ext目錄下和工程輸出目錄下的TestBean對應的class文件,然后再運行測試代碼,則將會有ClassNotFoundException異常拋出。有關這個問題,大家可以在java.lang.ClassLoader中的loadClass(String name, boolean resolve)方法中設置相應斷點運行測試三進行調試,會發現findBootstrapClass0()會拋出異常,然后在下面的findClass方法中被加載,當前運行的類加載器正是擴展類加載器(sun.misc.Launcher$ExtClassLoader),這一點可以通過JDT中變量視圖查看驗證。

    3       java程序動態擴展方式

    Java連接模型允許用戶運行時擴展引用程序,既可以通過當前虛擬機中預定義的加載器加載編譯時已知的類或者接口,又允許用戶自行定義類裝載器,在運行時動態擴展用戶的程序。通過用戶自定義的類裝載器,你的程序可以裝載在編譯時并不知道或者尚未存在的類或者接口,并動態連接它們并進行有選擇的解析。

           運行時動態擴展java應用程序有如下兩個途徑:

    3.1    調用java.lang.Class.forName(…)

    這個方法其實在前面已經討論過,在后面的問題2解答中說明了該方法調用會觸發那個類加載器開始加載任務。這里需要說明的是多參數版本的forName(…)方法:

    public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException

    這里的initialize參數是很重要的,可以覺得被加載同時是否完成初始化的工作(說明: 單參數版本的forName方法默認是不完成初始化的).有些場景下,需要將initialize設置為true來強制加載同時完成初始化,例如典型的就是利用DriverManager進行JDBC驅動程序類注冊的問題,因為每一個JDBC驅動程序類的靜態初始化方法都用DriverManager注冊驅動程序,這樣才能被應用程序使用,這就要求驅動程序類必須被初始化,而不單單被加載.

    3.2    用戶自定義類加載器

    通過前面的分析,我們可以看出,除了和本地實現密切相關的啟動類加載器之外,包括標準擴展類加載器和系統類加載器在內的所有其他類加載器我們都可以當做自定義類加載器來對待,唯一區別是是否被虛擬機默認使用。前面的內容中已經對java.lang.ClassLoader抽象類中的幾個重要的方法做了介紹,這里就簡要敘述一下一般用戶自定義類加載器的工作流程吧(可以結合后面問題解答一起看):

    1、首先檢查請求的類型是否已經被這個類裝載器裝載到命名空間中了,如果已經裝載,直接返回;否則轉入步驟2

    2、委派類加載請求給父類加載器(更準確的說應該是雙親類加載器,真個虛擬機中各種類加載器最終會呈現樹狀結構),如果父類加載器能夠完成,則返回父類加載器加載的Class實例;否則轉入步驟3

    3、調用本類加載器的findClass)方法,試圖獲取對應的字節碼,如果獲取的到,則調用defineClass)導入類型到方法區;如果獲取不到對應的字節碼或者其他原因失敗,返回異常給loadClass), loadClass)轉拋異常,終止加載過程(注意:這里的異常種類不止一種)。

           (說明:這里說的自定義類加載器是指JDK 1.2以后版本的寫法,即不覆寫改變java.lang.loadClass(…)已有委派邏輯情況下)

    4       常見問題分析:

    4.1    由不同的類加載器加載的指定類型還是相同的類型嗎?

    Java中,一個類用其完全匹配類名(fully qualified class name)作為標識,這里指的完全匹配類名包括包名和類名。但在JVM中一個類用其全名和一個加載類ClassLoader的實例作為唯一標識,不同類加載器加載的類將被置于不同的命名空間.我們可以用兩個自定義類加載器去加載某自定義類型(注意,不要將自定義類型的字節碼放置到系統路徑或者擴展路徑中,否則會被系統類加載器或擴展類加載器搶先加載),然后用獲取到的兩個Class實例進行java.lang.Object.equals)判斷,將會得到不相等的結果。這個大家可以寫兩個自定義的類加載器去加載相同的自定義類型,然后做個判斷;同時,可以測試加載java.*類型,然后再對比測試一下測試結果。

    4.2    在代碼中直接調用Class.forNameString name)方法,到底會觸發那個類加載器進行類加載行為?

    Class.forName(String name)默認會使用調用類的類加載器來進行類加載。我們直接來分析一下對應的jdk的代碼:

    //java.lang.Class.java

           publicstatic Class<?> forName(String className)throws ClassNotFoundException {

    return forName0(className, true, ClassLoader.getCallerClassLoader());

    }

    //java.lang.ClassLoader.java

    // Returns the invoker's class loader, or null if none.

    static ClassLoader getCallerClassLoader() {

                  // 獲取調用類(caller)的類型

            Class caller = Reflection.getCallerClass(3);

                  // This can be null if the VM is requesting it

            if (caller == null) {

                returnnull;

            }

            // 調用java.lang.Class中本地方法獲取加載該調用類(caller)的ClassLoader

            return caller.getClassLoader0();

    }

    //java.lang.Class.java

    //虛擬機本地實現,獲取當前類的類加載器,前面介紹的ClassgetClassLoader()也使用此方法

    native ClassLoader getClassLoader0();

    4.3    在編寫自定義類加載器時,如果沒有設定父加載器,那么父加載器是?

    前面講過,在不指定父類加載器的情況下,默認采用系統類加載器。可能有人覺得不明白,現在我們來看一下JDK對應的代碼實現。眾所周知,我們編寫自定義的類加載器直接或者間接繼承自java.lang.ClassLoader抽象類,對應的無參默認構造函數實現如下:

    //摘自java.lang.ClassLoader.java

    protected ClassLoader() {

               SecurityManager security = System.getSecurityManager();

               if (security != null) {

                   security.checkCreateClassLoader();

               }

               this.parent = getSystemClassLoader();

               initialized = true;

    }

    我們再來看一下對應的getSystemClassLoader()方法的實現:

    privatestaticsynchronizedvoid initSystemClassLoader() {

               //...

               sun.misc.Launcher l = sun.misc.Launcher.getLauncher();

               scl = l.getClassLoader();

               //...

    }

    我們可以寫簡單的測試代碼來測試一下:

    System.out.println(sun.misc.Launcher.getLauncher().getClassLoader());

    本機對應輸出如下:

    sun.misc.Launcher$AppClassLoader@197d257

    所以,我們現在可以相信當自定義類加載器沒有指定父類加載器的情況下,默認的父類加載器即為系統類加載器。同時,我們可以得出如下結論:

    即時用戶自定義類加載器不指定父類加載器,那么,同樣可以加載如下三個地方的類:

    1.    <Java_Runtime_Home>/lib下的類

    2.    < Java_Runtime_Home >/lib/ext下或者由系統變量java.ext.dir指定位置中的類

    3.    當前工程類路徑下或者由系統變量java.class.path指定位置中的類

    4.4    在編寫自定義類加載器時,如果將父類加載器強制設置為null,那么會有什么影響?如果自定義的類加載器不能加載指定類,就肯定會加載失敗嗎?

    JVM規范中規定如果用戶自定義的類加載器將父類加載器強制設置為null,那么會自動將啟動類加載器設置為當前用戶自定義類加載器的父類加載器(這個問題前面已經分析過了)同時,我們可以得出如下結論:

    即時用戶自定義類加載器不指定父類加載器,那么,同樣可以加載到<Java_Runtime_Home>/lib下的類,但此時就不能夠加載<Java_Runtime_Home>/lib/ext目錄下的類了。

        說明:問題3和問題4的推斷結論是基于用戶自定義的類加載器本身延續了java.lang.ClassLoader.loadClass)默認委派邏輯,如果用戶對這一默認委派邏輯進行了改變,以上推斷結論就不一定成立了,詳見問題5

    4.5    編寫自定義類加載器時,一般有哪些注意點?

    1.      一般盡量不要覆寫已有的loadClass)方法中的委派邏輯

    一般在JDK 1.2之前的版本才這樣做,而且事實證明,這樣做極有可能引起系統默認的類加載器不能正常工作。JVM規范和JDK文檔中(1.2或者以后版本中),都沒有建議用戶覆寫loadClass(…)方法,相比而言,明確提示開發者在開發自定義的類加載器時覆寫findClass(…)邏輯。舉一個例子來驗證該問題:

    //用戶自定義類加載器WrongClassLoader.Java(覆寫loadClass邏輯)

    publicclassWrongClassLoaderextends ClassLoader {

            public Class<?> loadClass(String name) throws ClassNotFoundException {

                returnthis.findClass(name);

            }

            protected Class<?> findClass(String name) throws ClassNotFoundException {

                //假設此處只是到工程以外的特定目錄D:/library下去加載類

                具體實現代碼省略

            }

    }

        通過前面的分析我們已經知道,用戶自定義類加載器(WrongClassLoader)的默

           認的類加載器是系統類加載器,但是現在問題4種的結論就不成立了。大家可以簡

           單測試一下,現在<Java_Runtime_Home>/lib< Java_Runtime_Home >/lib/ext和工

           程類路徑上的類都加載不上了。

           //問題5測試代碼一

    publicclass WrongClassLoaderTest {

            publicstaticvoid main(String[] args) {

               try {

                   WrongClassLoader loader = new WrongClassLoader();

                   Class classLoaded = loader.loadClass("beans.Account");

                   System.out.println(classLoaded.getName());

                   System.out.println(classLoaded.getClassLoader());

               } catch (Exception e) {

                   e.printStackTrace();

               }

            }

    }

    (說明:D:"classes"beans"Account.class物理存在的)

    輸出結果:

    java.io.FileNotFoundException: D:"classes"java"lang"Object.class (系統找不到指定的路徑。)

        at java.io.FileInputStream.open(Native Method)

        at java.io.FileInputStream.<init>(FileInputStream.java:106)

        at WrongClassLoader.findClass(WrongClassLoader.java:40)

        at WrongClassLoader.loadClass(WrongClassLoader.java:29)

        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)

        at java.lang.ClassLoader.defineClass1(Native Method)

        at java.lang.ClassLoader.defineClass(ClassLoader.java:620)

        at java.lang.ClassLoader.defineClass(ClassLoader.java:400)

        at WrongClassLoader.findClass(WrongClassLoader.java:43)

        at WrongClassLoader.loadClass(WrongClassLoader.java:29)

        at WrongClassLoaderTest.main(WrongClassLoaderTest.java:27)

    Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object

        at java.lang.ClassLoader.defineClass1(Native Method)

        at java.lang.ClassLoader.defineClass(ClassLoader.java:620)

        at java.lang.ClassLoader.defineClass(ClassLoader.java:400)

        at WrongClassLoader.findClass(WrongClassLoader.java:43)

        at WrongClassLoader.loadClass(WrongClassLoader.java:29)

        at WrongClassLoaderTest.main(WrongClassLoaderTest.java:27)

    這說明,連要加載的類型的超類型java.lang.Object都加載不到了。這里列舉的由于覆寫loadClass)引起的邏輯錯誤明顯是比較簡單的,實際引起的邏輯錯誤可能復雜的多。

    //問題5測試二

    //用戶自定義類加載器WrongClassLoader.Java(不覆寫loadClass邏輯)

    publicclassWrongClassLoaderextends ClassLoader {

            protected Class<?> findClass(String name) throws ClassNotFoundException {

                //假設此處只是到工程以外的特定目錄D:/library下去加載類

                具體實現代碼省略

            }

    }

    將自定義類加載器代碼WrongClassLoader.Java做以上修改后,再運行測試代碼,輸出結果如下:

    beans.Account

    WrongClassLoader@1c78e57

    這說明,beans.Account加載成功,且是由自定義類加載器WrongClassLoader加載。

    這其中的原因分析,我想這里就不必解釋了,大家應該可以分析的出來了。

    2.      2、正確設置父類加載器

    通過上面問題4和問題5的分析我們應該已經理解,個人覺得這是自定義用戶類加載器時最重要的一點,但常常被忽略或者輕易帶過。有了前面JDK代碼的分析作為基礎,我想現在大家都可以隨便舉出例子了。

    3.      3、保證findClassString )方法的邏輯正確性

    事先盡量準確理解待定義的類加載器要完成的加載任務,確保最大程度上能夠獲取到對應的字節碼內容。

    4.6    如何在運行時判斷系統類加載器能加載哪些路徑下的類?

    一是可以直接調用ClassLoader.getSystemClassLoader()或者其他方式獲取到系統類加載器(系統類加載器和擴展類加載器本身都派生自URLClassLoader),調用URLClassLoader中的getURLs()方法可以獲取到;

    二是可以直接通過獲取系統屬性java.class.path 來查看當前類路徑上的條目信息 System.getProperty("java.class.path")

    4.7    如何在運行時判斷標準擴展類加載器能加載哪些路徑下的類?

    方法之一:

    try {
                   URL[] extURLs = ((URLClassLoader)ClassLoader.getSystemClassLoader().getParent()).getURLs();

                  for (int i = 0; i < extURLs.length; i++) {

                         System.out.println(extURLs[i]);

                  }

           } catch (Exception e) {//…}

           本機對應輸出如下:

    file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/dnsns.jar

    file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/localedata.jar

    file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jar

    file:/D:/DEMO/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar

    5       總結:

    寫這篇文章的初衷是通過分析JDK相關代碼來驗證一些加載規則,核心就是借助雙親委派機制來分析一個加載請求處理的主要過程,所列舉的幾個簡單的例子實際意義不大,因為遇到的情況往往比例子情況復雜的多。

    下一篇文章,我會重點分析一下Eclipse的插件類加載器,并分析一下插件環境下的類加載和普通java應用場景中的類加載有什么不同,并會提供一個比較完整的類加載器。

    插件類加載器就是一個由eclipse開發的一個用戶自定義類加載器,所以分析時候用到的一些基本的東西都是在這篇文章中涉及到的。

        這篇文章寫的時候時間比較緊,亂糟糟的,大家見諒。中間參考了JVM規范、jdk文檔和代碼、《Inside Java Virtual Machine》一書等資料。

        文章中肯定包含了一些錯誤,歡迎指出,謝謝!



    本博客中的所有文章、隨筆除了標題中含有引用或者轉載字樣的,其他均為原創。轉載請注明出處,謝謝!

    posted on 2008-08-08 10:19 zhuxing 閱讀(39872) 評論(14)  編輯  收藏 所屬分類: Java

    評論

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    前兩個圖片看不見.
    2008-08-08 14:16 | xpf7622

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    不錯 難得有寫這么深入的中文文章 頂!!
    2008-08-30 11:21 | advincenting

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    高手啊!
    真正的高手啊!
    前輩搞了多少年JAVA了,現在在什么公司做架構?
    2008-09-19 22:56 | 家佳

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    哥,前面幾張圖看不到
    2008-09-24 20:32 | BlackGabriel

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    很黃,很暴力,真的非常好!高手中的高手啊……微軟的架構師吧!
    2010-12-17 15:35 | zw

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    膜拜中...
    2011-10-21 10:08 | ywm

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    真心膜拜啊。。。。
    2012-05-24 21:56 | ip

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    寫的很好,轉了,但是文中有一處錯誤:"單參數版本的forName方法默認是不完成初始化的",正好說反了,單參數版的默認是進行初始化的。
    2012-06-20 23:53 | 孫欽旺

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    高手,頂
    2013-03-27 17:30 | zqj

    # re: 【原創】Java類加載原理解析[未登錄]  回復  更多評論   

    高淫啊,我要什么時候才能到你這種水平呢
    2013-03-29 20:36 | kevin

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    @孫欽旺
    看的很仔細,完全同意,估計樓主記反了,Class.forName單個參數是默認需要初始化的,而loadClass當個參數默認是不會初始化的,第二個參數默認是fasle。
    2013-09-20 15:28 | weisongbai

    # re: 【原創】Java類加載原理解析[未登錄]  回復  更多評論   

    恩,講解的很清晰,不錯的文章!
    2013-12-09 14:34 | David

    # re: 【原創】Java類加載原理解析  回復  更多評論   

    寫的非常好,搞清了以前不明白的一些東西,轉載了
    2014-06-17 09:50 | 獅子愛睡覺

    # re: 【原創】Java類加載原理解析[未登錄]  回復  更多評論   

    或者其他方式獲取到系統類加載器(系統類加載器和擴展類加載器本身都派生自URLClassLoader)

    怎么理解???

    2016-06-14 16:11 | 111
    主站蜘蛛池模板: 日韩在线免费看网站| 亚洲不卡av不卡一区二区| 一级毛片免费视频网站| 亚洲成AV人在线播放无码| 久久午夜免费视频| 一个人晚上在线观看的免费视频| 亚洲第一精品在线视频| 四虎www成人影院免费观看| 东北美女野外bbwbbw免费| 亚洲乱码一区av春药高潮| 国产精品亚洲产品一区二区三区| 亚洲免费视频在线观看| 毛片免费全部播放一级| a级男女仿爱免费视频| 亚洲日本人成中文字幕| 亚洲精品乱码久久久久久久久久久久 | 亚洲精品国产日韩无码AV永久免费网 | 国产大片免费天天看| 久久精品国产精品亚洲下载| 免费人成大片在线观看播放电影| 亚洲高清在线播放| 免费a级毛片无码a∨性按摩| 国产91色综合久久免费| 精品久久久久久无码免费| 亚洲人成网亚洲欧洲无码| 亚洲国产精品久久久久久| 亚洲Av无码乱码在线播放| 西西大胆无码视频免费| 久久久久成人精品免费播放动漫| 猫咪www免费人成网站| 亚洲人成小说网站色| 日木av无码专区亚洲av毛片| 久久久久亚洲精品中文字幕| 永久免费看bbb| 最近最好的中文字幕2019免费 | 亚洲综合一区二区精品久久| 在线观看免费人成视频| 99在线免费观看视频| 热99RE久久精品这里都是精品免费 | 美女被免费视频网站a国产| 91免费人成网站在线观看18|