雙親委派模型

mysoul8021發表於2019-01-22

類載入流程

類載入流程,先載入Bootstrap ClassLoader 啟動類載入即最頂層的載入類。這部分由C++ 編寫。
繼續再次載入Extention ClassLoader 擴充套件類載入器由Bootstrap ClassLoader載入,載入進入記憶體。
繼續再次載入Application ClassLoader 應用類載入器,即系統即在此,載入當前應用下的類
最後再次載入ClassLoader類

雙親委派模型

該模型是先檢查指定名稱的類是否已經載入過,如果載入進入記憶體,不載入直接返回,如果沒有載入過,判斷是否有父類載入器,如果擁有父類載入器,那麼將會直接將權利移交給父類,由父類代理當前類進行載入該類。或者是呼叫C++的bootstrap類載入器來載入該類
最後如果三者都沒有找到類,那麼直接呼叫當前類載入器的findClass方法來完成類載入。
即,如果需要需要載入自定義的類,那麼就需要重寫findClass方法。

即,如果需要將當前類載入進入,那麼就需要重寫findClass方法,若未找到這幾種類,則會自動呼叫findClass方法。

呼叫過程

先載入父類,若父類未載入,繼續呼叫父類,直到bootstrap檢視是否已經載入,如果此時都未載入類,那麼將會使用自定義的ClassLoader 然後呼叫自定義的ClassLoader的findClass方法,用於將位元組碼載入進入記憶體。最後返回該class的描述符

栗子

public class Test {
    public void helloWorld(){
        System.out.println("me loader" + getClass().getClassLoader().getClass());
        // 先呼叫getClass獲取當前類的物件的描述,然後再次呼叫getClassLoader()獲取載入的父類,再次呼叫getClass()獲取載入進入的父類的名稱
    }
}

此時如果該類這這個專案裡的裡的話,會由Application載入當前應用類下的類。

由類的載入過程可知,當未找到類的時候,會載入類的ClassLoader類,此時需要定義一個類,讓該類繼承ClassLoader類,由於該類是ClassLoader的子類,此時會自動載入該類,由於該類不在記憶體當中,所以需要使用static,讓其一開始載入進入記憶體當中。
程式碼如下

import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
    // 需要使用static讓其載入進入記憶體
    static class myClassLoader extends ClassLoader{
        private String classPath;  // 獲取當前類的在磁碟中儲存的地址
        // 通過建構函式將地址注入
        public myClassLoader(String classPath){
            this.classPath = classPath;
        }
        // 將檔案內容載入進入記憶體
        private byte[] loadByte(String name) throws Exception{
            // 獲取一個輸入流,
            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            // 獲取長度
            int len = fis.available();
            // 定義byte陣列
            byte[] data = new byte[len];
            // 載入進入記憶體
            fis.read(data);
            // 關閉流
            fis.close();
            return data;
        }
        // 重寫findClass方法,讓載入的時候呼叫findClass方法
        protected Class<?> findClass(String name) throws ClassNotFoundException{
            try{
                // 讀取檔案到陣列中
                byte[] data = loadByte(name);
                // 將位元組碼載入進入記憶體當中
                return defineClass(name, data, 0, data.length);
            }catch(Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        // 先初始化該類
        myClassLoader classLoader = new myClassLoader("/home/ming");
        // 此時會呼叫findClass載入Test.class載入進入記憶體當中
        Class clazz = classLoader.loadClass("Test");
        // 例項化該類物件
        Object obj = clazz.newInstance();
        // 獲取clazz該類方法中名稱為hello,引數為空的方法。
        Method helloMethod = clazz.getDeclaredMethod("helloWorld", null);
       // 呼叫該方法
        // 呼叫obj類中的helloMethod,引數為空的方法。
       helloMethod.invoke(obj, null);

    }
}

實驗結果

me loaderclass Main$myClassLoader

Process finished with exit code 0


相關文章