手把手叫你寫類加載器。
了解了類加載器的雙親委派機制, 也知道了雙親委派機制的原理,接下來就是檢驗我們學習是否扎實了,來自定義一個類加載器
一. 回顧類加載器的原理

還是這張圖,類加載器的入口是c++調用java代碼創建了JVM啟動器,其中的一個啟動器是sun.misc.Launcher啟動器。這個啟動器啟動並加載的AppClassLoader和ExtClassLoader。然后調用launcher.getClassLoader()方法獲取loader對象, loader對象本質是一個ClassLoader,然后調用了ClassLoader的loadClass("...")方法加載類。也是在loadClass("...")方法里實現了雙親委派機制。
詳細原理參考文章:https://www.cnblogs.com/ITPower/p/15363400.html
二、自定義類加載器分析
對於類加載器, 我們知道他的重點是loadClass(...)方法, 里面的雙親委派機制也是在loadClass方法里面實現的. loadClass方法里面實際上去加載類的是findClass()方法. 對於我們自定義的類加載器來說需要做到兩點即可
-
這個自定義的類加載器繼承自ClassLoader
-
這個類加載器要重寫ClassLoader類中的findClass()方法
另外我們還可以參考AppClassLoader和ExtClassLoader來寫。
三、自定義類加載器實現
下面我自己定義了一個類加載器
第一步:自定義類加載器繼承自ClassLoader抽象類,然后定義一個構造方法, 用來接收要加載的類名

第二步:重寫核心方法findClass(String name)

這里有兩步操作,
第一個是: 從類路徑中讀取要加載類的文件內容, 自定義
第二個是: 調用構造類的方法, 調用的系統的defineClass
接下來看看自定義的loadByte是如何實現的

這里的實現就是找到類, 並且將類的內容讀取出來, 轉換成二進制的字節碼, 返回
最后一部分就是如何調用了.

用類加載器加載類, 然后實例化, 使用反射機制調用User1 的方法sout
package com.lxl.jvm;
public class User1 {
public void sout() {
System.out.println("進入到User1");
}
}
這里面System.out.println(clazz.getClassLoader().getClass().getName()); 獲取當前類的類加載器, 猜一猜這里打印的會是誰?

看到了么? 是AppClassLoader, 為什么呢?
原因是我的項目里已經有一個類User1了

我們自定義類加載器的父類是AppClassLoader. 而程序代碼中的User1剛好是被AppClassLoader加載, 因為找到了,所以就不會再去我們指定的文件夾中查找了
這就是類的雙親委派機制的特點.
那么如果我們將項目中的User1類刪除掉, 這是類加載器是誰呢? 當然就是我們自定義的類加載器了.
那么問題來了, 自定義類加載器的父類為什么是AppClassLoader呢?
四. 分析自定義類加載的父類為什么是appClassLoader?
我們來看一下源碼
我們自定義的類加載器, 繼承自ClassLoader類加載器, 那么在調用自定義類加載器的構造方法之前, 應該先加載父類ClassLoader的無參構造函數.
首先會執行ClassLoader的無參的構造方法.

而無參的構造方法會調用自身的構造方法

里面有一個parent, 我們就是要看看這個parent到底是誰呢. 來看看getSystemClassLoader()方法


之前我們已經研究過getClassLoader()這個方法了, 這里面定義的loadClass是誰呢?就是AppClassLoader.


這就是為什么自定義class類加載器的父類是AppClassLoader的原因了。