1、ClassLoader與現有類加載器的關系
ClassLoader與現有類加載器的關系:

ClassLoader是一個抽象類。如果我們給定了一個類的二進制名稱,類加載器應嘗試去定位或生成構成定義類的數據。一種典型的策略是將給定的二進制名稱轉換為文件名,然后去文件系統中讀取這個文件名所對應的class文件。
2、ClassLoader的主要方法
抽象類ClassLoader的主要方法:(內部沒有抽象方法)
public final ClassLoader getParent()
//返回該類加載器的超類加載器
public Class<?> loadClass(String name) throws ClassNotFoundException
加載名稱為name的類,返回結果為java.lang.Class類的實例。
如果找不到類,則返回 ClassNotFoundException 異常。該方法中的邏輯就是雙親委派模式的實現。
protected Class<?> findClass(String name) throws ClassNotFoundException
// 查找二進制名稱為name的類,返回結果為java.lang.Class類的實例。這是一個受保護的方法,JVM鼓勵我們重寫此方法,需要自定義加載器遵循雙親委托機制,該方法會在檢查完父類加載器之后被loadClass()方法調用。
在JDK1.2之前,在自定義類加載時,總會去繼承ClassLoader類并重寫loadClass方法,從而實現自定義的類加載類。但是在JDK1.2之后已不再建議用戶去覆蓋loadClass()方法,而是建議把自定義的類加載邏輯寫在findClass()方法中,從前面的分析可知,findClass()方法是在loadClass()方法中被調用的,當loadClass()方法中父加載器加載失敗后,則會調用自己的findClass()方法來完成類加載,這樣就可以保證自定義的類加載器也符合雙親委托模式。
需要注意的是ClassLoader類中并沒有實現findClass()方法的具體代碼邏輯,取而代之的是拋出ClassNotFoundException異常,同時應該知道的是findClass方法通常是和defineClass方法一起使用的。一般情況下,在自定義類加載器時,會直接覆蓋ClassLoader的findClass()方法并編寫加載規則,取得要加載類的字節碼后轉換成流,然后調用defineClass()方法生成類的Class對象。
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
//根據給定的字節數組b轉換為Class的實例,off和len參數表示實際Class信息在byte數組中的位置和長度,其中byte數組b是ClassLoader從外部獲取的。這是受保護的方法,只有在自定義ClassLoader子類中可以使用。
defineClass()方法是用來將byte字節流解析成JVM能夠識別的Class對象(ClassLoader中已實現該方法邏輯),通過這個方法不僅能夠通過class文件實例化class對象,也可以通過其他方式實例化class對象,如通過網絡接收一個類的字節碼,然后轉換為byte字節流創建對應的Class對象。
defineClass()方法通常與findClass()方法一起使用,一般情況下,在自定義類加載器時,會直接覆蓋ClassLoader的findClass()方法并編寫加載規則,取得要加載類的字節碼后轉換成流,然后調用defineClass()方法生成類的Class對象
例如:
protected Class<?> findClass(String name) throws ClassNotFoundException {// 獲取類的字節數組byte[] classData = getClassData(name);if (classData == null) {throw new ClassNotFoundException();} else {//使用defineClass生成class對象return defineClass(name, classData, 0, classData.length);}
}
protected final void resolveClass(Class<?> c)
// 鏈接指定的一個Java類。使用該方法可以使用類的Class對象創建完成的同時也被解析。前面我們說鏈接階段主要是對字節碼進行驗證,為類變量分配內存并設置初始值同時將字節碼文件中的符號引用轉換為直接引用。
protected final Class<?> findLoadedClass(String name)
//查找名稱為name的已經被加載過的類,返回結果為java.lang.Class類的實例。這個方法是final方法,無法被修改。
private final ClassLoader parent;
//它也是一個ClassLoader的實例,這個字段所表示的ClassLoader也稱為這個ClassLoader的雙親。在類加載的過程中,ClassLoader可能會將某些請求交予自己的雙親處理。
loadClass()的剖析
ClassLoader.getSystemClassLoader().loadClass("com.atguig.java.User");
//測試代碼
涉及到對如下方法的調用:
protected Class<?> loadClass(String name, boolean resolve) //resolve:true-加載class的同時進行解析操作。throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) { //同步操作,保證只能加載一次。//首先,在緩存中判斷是否已經加載同名的類。Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {//獲取當前類加載器的父類加載器。if (parent != null) {//如果存在父類加載器,則調用父類加載器進行類的加載c = parent.loadClass(name, false);} else { //parent為null:父類加載器是引導類加載器c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) { //當前類的加載器的父類加載器未加載此類 or 當前類的加載器未加載此類// 調用當前ClassLoader的findClass()long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {//是否進行解析操作resolveClass(c);}return c;}
}