更多課程 選擇中心
        Java培訓

        400-111-8989

        Java培訓 > Java教程  > 正文

        java類加載機制詳解

        • 發布:Java培訓
        • 來源:Java教程
        • 時間:2017-08-29 12:00

        類加載機制是 Java 語言的一大亮點,使得 Java 類可以被動態加載到 Java 虛擬機中。

        這次我們拋開術語和概念,從例子入手,由淺入深地講解 Java 的類加載機制。

        本文涉及知識點:雙親委托機制、BootstrapClassLoader、ExtClassLoader、AppClassLoader、自定義網絡類加載器等

        文章涉及代碼:

        https://github.com/wingjay/HelloJava/blob/master/common/src/classloader/HelloClassLoader.java

        什么是 Java 類加載機制?

        Java 虛擬機一般使用 Java 類的流程為:首先將開發者編寫的 Java 源代碼(.java文件)編譯成 Java 字節碼(.class文件),然后類加載器會讀取這個 .class 文件,并轉換成 java.lang.Class 的實例。有了該 Class 實例后,Java 虛擬機可以利用 newInstance 之類的方法創建其真正對象了。

        ClassLoader 是 Java 提供的類加載器,絕大多數的類加載器都繼承自 ClassLoader,它們被用來加載不同來源的 Class 文件。

        Class 文件有哪些來源呢?

        上文提到了 ClassLoader 可以去加載多種來源的 Class,那么具體有哪些來源呢?

        首先,最常見的是開發者在應用程序中編寫的類,這些類位于項目目錄下;

        然后,有 Java 內部自帶的核心類如 java.lang、java.math、java.io 等 package 內部的類,位于 $JAVA_HOME/jre/lib/ 目錄下,如 java.lang.String 類就是定義在 $JAVA_HOME/jre/lib/rt.jar 文件里;

        另外,還有 Java 核心擴展類,位于 $JAVA_HOME/jre/lib/ext 目錄下。開發者也可以把自己編寫的類打包成 jar 文件放入該目錄下;

        最后還有一種,是動態加載遠程的 .class 文件。

        既然有這么多種類的來源,那么在 Java 里,是由某一個具體的 ClassLoader 來統一加載呢?還是由多個 ClassLoader 來協作加載呢?

        哪些 ClassLoader 負責加載上面幾類 Class?

        實際上,針對上面四種來源的類,分別有不同的加載器負責加載。

        首先,我們來看級別最高的 Java 核心類,即$JAVA_HOME/jre/lib 里的核心 jar 文件。這些類是 Java 運行的基礎類,由一個名為 BootstrapClassLoader 加載器負責加載,它也被稱作 根加載器/引導加載器。注意,BootstrapClassLoader 比較特殊,它不繼承 ClassLoader,而是由 JVM 內部實現;

        然后,需要加載 Java 核心擴展類,即 $JAVA_HOME/jre/lib/ext 目錄下的 jar 文件。這些文件由 ExtensionClassLoader 負責加載,它也被稱作 擴展類加載器。當然,用戶如果把自己開發的 jar 文件放在這個目錄,也會被 ExtClassLoader 加載;

        接下來是開發者在項目中編寫的類,這些文件將由 AppClassLoader 加載器進行加載,它也被稱作 系統類加載器 System ClassLoader;

        最后,如果想遠程加載如(本地文件/網絡下載)的方式,則必須要自己自定義一個 ClassLoader,復寫其中的 findClass() 方法才能得以實現。

        因此能看出,Java 里提供了至少四類 ClassLoader 來分別加載不同來源的 Class。

        那么,這幾種 ClassLoader 是如何協作來加載一個類呢?

        這些 ClassLoader 以何種方式來協作加載 String 類呢?

        String 類是 Java 自帶的最常用的一個類,現在的問題是,JVM 將以何種方式把 String class 加載進來呢?

        我們來猜想下。

        首先,String 類屬于 Java 核心類,位于 $JAVA_HOME/jre/lib 目錄下。有的朋友會馬上反應過來,上文中提過了,該目錄下的類會由 BootstrapClassLoader 進行加載。沒錯,它確實是由 BootstrapClassLoader 進行加載。但,這種回答的前提是你已經知道了 String 在 $JAVA_HOME/jre/lib 目錄下。

        那么,如果你并不知道 String 類究竟位于哪呢?或者我希望你去加載一個 unknown 的類呢?

        有的朋友這時會說,那很簡單,只要去遍歷一遍所有的類,看看這個 unknown 的類位于哪里,然后再用對應的加載器去加載。

        是的,思路很正確。那應該如何去遍歷呢?

        比如,可以先遍歷用戶自己寫的類,如果找到了就用 AppClassLoader 去加載;否則去遍歷 Java 核心類目錄,找到了就用 BootstrapClassLoader 去加載,否則就去遍歷 Java 擴展類庫,依次類推。

        這種思路方向是正確的,不過存在一個漏洞。

        假如開發者自己偽造了一個 java.lang.String 類,即在項目中創建一個包java.lang,包內創建一個名為 String 的類,這完全可以做到。那如果利用上面的遍歷方法,是不是這個項目中用到的 String 不是都變成了這個偽造的 java.lang.String 類嗎?如何解決這個問題呢?

        解決方法很簡單,當查找一個類時,優先遍歷最高級別的 Java 核心類,然后再去遍歷 Java 核心擴展類,最后再遍歷用戶自定義類,而且這個遍歷過程是一旦找到就立即停止遍歷。

        在 Java 中,這種實現方式也稱作 雙親委托。其實很簡單,把 BootstrapClassLoader 想象為核心高層領導人, ExtClassLoader 想象為中層干部, AppClassLoader 想象為普通公務員。每次需要加載一個類,先獲取一個系統加載器 AppClassLoader 的實例(ClassLoader.getSystemClassLoader()),然后向上級層層請求,由最上級優先去加載,如果上級覺得這些類不屬于核心類,就可以下放到各子級負責人去自行加載。

        如下圖所示:

        Java類加載機制詳解

        真的是按照雙親委托方式進行類加載嗎?

        下面通過幾個例子來驗證上面的加載方式。

        開發者自定義的類會被 AppClassLoader 加載嗎?

        在項目中創建一個名為 MusicPlayer 的類文件,內容如下:

        java類加載機制詳解

        然后來加載 MusicPlayer。

        java類加載機制詳解

        打印結果為:

        java類加載機制詳解

        可以驗證,MusicPlayer 是由 AppClassLoader 進行的加載。

        驗證 AppClassLoader 的雙親真的是 ExtClassLoader 和 BootstrapClassLoader 嗎?

        這時發現 AppClassLoader 提供了一個 getParent() 的方法,來打印看看都是什么。

        java類加載機制詳解

        打印結果為:

        java類加載機制詳解

        首先能看到 ExtClassLoader 確實是 AppClassLoader 的雙親,不過卻沒有看到 BootstrapClassLoader。事實上,上文就提過, BootstrapClassLoader比較特殊,它是由 JVM 內部實現的,所以 ExtClassLoader.getParent() = null。

        如果把 MusicPlayer 類挪到 $JAVA_HOME/jre/lib/ext 目錄下會發生什么?

        上文中說了,ExtClassLoader 會加載$JAVA_HOME/jre/lib/ext 目錄下所有的 jar 文件。那來嘗試下直接把 MusicPlayer 這個類放到 $JAVA_HOME/jre/lib/ext 目錄下吧。

        利用下面命令可以把 MusicPlayer.java 編譯打包成 jar 文件,并放置到對應目錄。

        java類加載機制詳解

        這時 MusicPlayer.jar 已經被放置與 $JAVA_HOME/jre/lib/ext 目錄下,同時把之前的 MusicPlayer 刪除,而且這一次刻意使用 AppClassLoader 來加載:

        java類加載機制詳解

        打印結果為:

        java類加載機制詳解

        說明即使直接用 AppClassLoader 去加載,它仍然會被 ExtClassLoader 加載到。

        從源碼角度真正理解雙親委托加載機制

        上面已經通過一些例子了解了雙親委托的一些特性了,下面來看一下它的實現代碼,加深理解。

        打開 ClassLoader 里的 loadClass() 方法,便是需要分析的源碼了。這個方法里做了下面幾件事:

        1. 檢查目標class是否曾經加載過,如果加載過則直接返回;

        2. 如果沒加載過,把加載請求傳遞給 parent 加載器去加載;

        3. 如果 parent 加載器加載成功,則直接返回;

        4. 如果 parent 未加載到,則自身調用 findClass() 方法進行尋找,并把尋找結果返回。

        代碼如下:

        java類加載機制詳解

        看完實現源碼相信能夠有更完整的理解。

        類加載器最酷的一面:自定義類加載器

        前面提到了 Java 自帶的加載器 BootstrapClassLoader、AppClassLoader和ExtClassLoader,這些都是 Java 已經提供好的。

        而真正有意思的,是 自定義類加載器,它允許我們在運行時可以從本地磁盤或網絡上動態加載自定義類。這使得開發者可以動態修復某些有問題的類,熱更新代碼。

        下面來實現一個網絡類加載器,這個加載器可以從網絡上動態下載 .class 文件并加載到虛擬機中使用。

        后面我還會寫作與 熱修復/動態更新 相關的文章,這里先學習 Java 層 NetworkClassLoader 相關的原理。

        1. 作為一個 NetworkClassLoader,它首先要繼承 ClassLoader;

        2. 然后它要實現ClassLoader內的 findClass() 方法。注意,不是loadClass()方法,因為ClassLoader提供了loadClass()(如上面的源碼),它會基于雙親委托機制去搜索某個 class,直到搜索不到才會調用自身的findClass(),如果直接復寫loadClass(),那還要實現雙親委托機制;

        3. 在 findClass() 方法里,要從網絡上下載一個 .class 文件,然后轉化成 Class 對象供虛擬機使用。

        具體實現代碼如下:

        java類加載機制詳解

        java類加載機制詳解

        這個類的作用是從網絡上(這里是本人的 local apache 服務器 http://localhost/java 上)目錄里去下載對應的 .class 文件,并轉換成 Class 返回回去使用。

        下面我們來利用這個 NetworkClassLoader 去加載 localhost 上的 MusicPlayer 類:

        1. 首先把 MusicPlayer.class 放置于 /Library/WebServer/Documents/java (MacOS)目錄下,由于 MacOS 自帶 apache 服務器,這里是服務器的默認目錄;

        2. 執行下面一段代碼:

        java類加載機制詳解

        3. 正常運行,加載 http://localhost/java/classloader/MusicPlayer.class成功。

        可以看出 NetworkClassLoader 可以正常工作,如果讀者要用的話,只要稍微修改 url 的拼接方式即可自行使用。

        小結

        類加載方式是 Java 上非常創新的一項技術,給未來的熱修復技術提供了可能。本文力求通過簡單的語言和合適的例子來講解其中雙親委托機制、自定義加載器等,并開發了自定義的NetworkClassLoader。

        當然,類加載是很有意思的技術,很難覆蓋所有知識點,比如不同類加載器加載同一個類,得到的實例卻不是同一個等等。

        感謝大家閱讀由java培訓機構分享的“java類加載機制詳解”希望對各位學員有所幫助,更多精彩內容請關注Java培訓官網

        免責聲明:本文由小編轉載自網絡,旨在分享提供閱讀,版權歸原作者所有,如有侵權請聯系我們進行刪除

        預約申請免費試聽課

        填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

        上一篇:一分鐘看懂常用的排序算法
        下一篇:什么是JWT VS Session
        12大要點讓你的Java開發所向披靡~

        12大要點讓你的Java開發所向披靡~

        學習Java最好的12本免費在線電子書

        學習Java最好的12本免費在線電子書

        Java常用日志框架介紹

        Java常用日志框架介紹

        一篇文章了解RPC框架原理

        一篇文章了解RPC框架原理

        • 掃碼領取資料

          回復關鍵字:視頻資料

          免費領取 達內課程視頻學習資料

        • 視頻學習QQ群

          添加QQ群:1143617948

          免費領取達內課程視頻學習資料

        Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

        選擇城市和中心
        貴州省

        福建省

        • 達內廈門軟件園中心
        廣西省

        海南省

        国产高清情侣视频2019年,限制电影福利在线观看,23伊人大香蕉 百度 好搜 搜狗
        <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>