類加載過程:加載->鏈接->初始化。鏈接過程又可分爲三步:驗證->準備->解析。java
一個非數組類的加載階段(加載階段獲取類的二進制字節流的動做)是可控性最強的階段,這一步咱們能夠去完成還能夠自定義類加載器去控制字節流的獲取方式(重寫一個類加載器的 loadClass()
方法)。數組類型不經過類加載器建立,它由 Java 虛擬機直接建立。git
全部的類都由類加載器加載,加載的做用就是將 .class文件加載到內存。github
JVM 中內置了三個重要的 ClassLoader,除了 BootstrapClassLoader 其餘類加載器均由 Java 實現且所有繼承自java.lang.ClassLoader
:數組
%JAVA_HOME%/lib
目錄下的jar包和類或者或被 -Xbootclasspath
參數指定的路徑中的全部類。%JRE_HOME%/lib/ext
目錄下的jar包和類,或被 java.ext.dirs
系統變量所指定的路徑下的jar包。每個類都有一個對應它的類加載器。系統中的 ClassLoder 在協同工做的時候會默認使用 雙親委派模型 。即在類加載的時候,系統會首先判斷當前類是否被加載過。已經被加載的類會直接返回,不然纔會嘗試加載。**加載的時候,首先會把該請求委派該父類加載器的 loadClass()
處理,所以全部的請求最終都應該傳送到頂層的啓動類加載器 BootstrapClassLoader
中。當父類加載器沒法處理時,才由本身來處理。**當父類加載器爲null時,會使用啓動類加載器 BootstrapClassLoader
做爲父類加載器。jvm
每一個類加載都有一個父類加載器,咱們經過下面的程序來驗證。ide
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@18b4aac2 The Parent of ClassLodarDemo's ClassLoader is sun.misc.Launcher$ExtClassLoader@1b6d3586 The GrandParent of ClassLodarDemo's ClassLoader is null
AppClassLoader
的父類加載器爲ExtClassLoader
ExtClassLoader
的父類加載器爲null,null並不表明ExtClassLoader
沒有父類加載器,而是 Bootstrap ClassLoader
。post
其實這個雙親翻譯的容易讓別人誤解,咱們通常理解的雙親都是父母,這裏的雙親更多地表達的是「父母這一輩」的人而已,並非說真的有一個 Mather ClassLoader 和一個 Father ClassLoader 。另外,類加載器之間的「父子」關係也不是經過繼承來體現的,是由「優先級」來決定。官方API文檔對這部分的描述以下:ui
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.this
雙親委派模型的實現代碼很是簡單,邏輯很是清晰,都集中在 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
。