類加載過程:加載->鏈接->初始化。鏈接過程又可分爲三步:驗證->準備->解析。java
那加載這一步作了什麼?
類加載過程的第一步,主要完成下面3件事情:web
經過全類名獲取定義此類的二進制字節流swift
將字節流所表明的靜態存儲結構轉換爲方法區的運行時數據結構數組
在內存中生成一個表明該類的 Class 對象,做爲方法區這些數據的訪問入口微信
虛擬機規範多上面這3點並不具體,所以是很是靈活的。好比:"經過全類名獲取定義此類的二進制字節流" 並無指明具體從哪裏獲取、怎樣獲取。好比:比較常見的就是從 ZIP 包中讀取(往後出現的JAR、EAR、WAR格式的基礎)、其餘文件生成(典型應用就是JSP)等等。數據結構
一個非數組類的加載階段(加載階段獲取類的二進制字節流的動做)是可控性最強的階段,這一步咱們能夠去完成還能夠自定義類加載器去控制字節流的獲取方式(重寫一個類加載器的 loadClass()
方法)。數組類型不經過類加載器建立,它由 Java 虛擬機直接建立。app
類加載器、雙親委派模型也是很是重要的知識點,這部份內容會在後面的問題中單獨介紹到。編輯器
加載階段和鏈接階段的部份內容是交叉進行的,加載階段還沒有結束,鏈接階段可能就已經開始了。ide
知道哪些類加載器?
JVM 中內置了三個重要的 ClassLoader,除了 BootstrapClassLoader 其餘類加載器均由 Java 實現且所有繼承自java.lang.ClassLoader
:源碼分析
BootstrapClassLoader(啓動類加載器) :最頂層的加載類,由C++實現,負責加載
%JAVA_HOME%/lib
目錄下的jar包和類或者或被-Xbootclasspath
參數指定的路徑中的全部類。ExtensionClassLoader(擴展類加載器) :主要負責加載目錄
%JRE_HOME%/lib/ext
目錄下的jar包和類,或被java.ext.dirs
系統變量所指定的路徑下的jar包。AppClassLoader(應用程序類加載器) :面向咱們用戶的加載器,負責加載當前應用classpath下的全部jar包和類。
雙親委派模型知道嗎?能介紹一下嗎?
雙親委派模型介紹
每個類都有一個對應它的類加載器。系統中的 ClassLoder 在協同工做的時候會默認使用 雙親委派模型 。即在類加載的時候,系統會首先判斷當前類是否被加載過。已經被加載的類會直接返回,不然纔會嘗試加載。加載的時候,首先會把該請求委派該父類加載器的 loadClass()
處理,所以全部的請求最終都應該傳送到頂層的啓動類加載器 BootstrapClassLoader
中。當父類加載器沒法處理時,才由本身來處理。當父類加載器爲null時,會使用啓動類加載器 BootstrapClassLoader
做爲父類加載器。
每一個類加載都有一個父類加載器,咱們經過下面的程序來驗證。
public class ClassLoaderDemo { public static void main(String[] args) { System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader()); System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent()); System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent()); }}
Output
ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@1b6d3586The GrandParent of ClassLodarDemo's ClassLoader is null
AppClassLoader
的父類加載器爲ExtClassLoader
ExtClassLoader
的父類加載器爲null,null並不表明ExtClassLoader
沒有父類加載器,而是 Bootstrap ClassLoader
。
其實這個雙親翻譯的容易讓別人誤解,咱們通常理解的雙親都是父母,這裏的雙親更多地表達的是「父母這一輩」的人而已,並非說真的有一個 Mather ClassLoader 和一個 Father ClassLoader 。另外,類加載器之間的「父子」關係也不是經過繼承來體現的,是由「優先級」來決定。官方API文檔對這部分的描述以下:
The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a "parent" class loader. When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.
雙親委派模型實現源碼分析
雙親委派模型的實現代碼很是簡單,邏輯很是清晰,都集中在 java.lang.ClassLoader
的 loadClass()
中,相關代碼以下所示。
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 { if (parent != null) {//父加載器不爲空,調用父加載器loadClass()方法處理 c = parent.loadClass(name, false); } else {//父加載器爲空,使用啓動類加載器 BootstrapClassLoader 加載 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //拋出異常說明父類加載器沒法完成加載請求 }
if (c == null) { 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; } }
雙親委派模型帶來了什麼好處呢?
雙親委派模型保證了Java程序的穩定運行,能夠避免類的重複加載(JVM 區分不一樣類的方式不只僅根據類名,相同的類文件被不一樣的類加載器加載產生的是兩個不一樣的類),也保證了 Java 的核心 API 不被篡改。若是不用沒有使用雙親委派模型,而是每一個類加載器加載本身的話就會出現一些問題,好比咱們編寫一個稱爲 java.lang.Object
類的話,那麼程序運行的時候,系統就會出現多個不一樣的 Object
類。
若是咱們不想用雙親委派模型怎麼辦?
爲了不雙親委託機制,咱們能夠本身定義一個類加載器,而後重載 loadClass()
便可。
如何自定義類加載器?
除了 BootstrapClassLoader
其餘類加載器均由 Java 實現且所有繼承自java.lang.ClassLoader
。若是咱們要自定義本身的類加載器,很明顯須要繼承 ClassLoader
。
![]()
你點的每一個「在看」,我都當成了喜歡
本文分享自微信公衆號 - Java學習提高(javaxuexitisheng)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。