ClassLoader簡單教程

ClassLoader教程

1. 簡介

Classloader的核心做用就是將編譯以後的class文件加載到jvm運行的內存當中。在jvm的規範當中,類加載器主要分爲三種:java

引導類加載器(BootClassLoader)數組

擴展類加載器(ExtClassLoader)緩存

系統類加載器(AppClassLoader)安全

2. 加載器分類

2. 1引導類加載器

引導類加載器主要加載的class是jdk自己的類庫(JAVA_HOME\lib目錄下的jar文件),這些類庫都是jdk核心的類庫(rt.jar),也是最重要的,所以由這個加載器去完成加載和初始化。而且這個加載器是使用C語言實現的。jvm

2.2 擴展類加載器

擴展類加載器的主要租用就是加載jdk的擴展包的jar文件(JAVA_HOME\lib\ext目下的jar文件),這些文件是JDK的擴展類庫。ide

2.3 系統類加載器

系統了加載器的核心做用就是加載classpath路徑下的class文件以及jar文件,這些類庫一般由開發人員本身編寫的,所以這些類都是經過系統類加載完成加載。測試

3. 委託模式

在執行類加載的時候,最早由系統類加載器開始,但它會把加載的執行權先交給擴展類加載器,一樣,擴展類加載器也會將加載的執行權交給引導類加載器,最終由引導類加載器開始加載,若是須要加載的類庫不是引導類加載器加載的範疇,那麼就會將加載權交回給擴展類加載器。一樣,若是類庫不是擴展類加載器加載的範疇,最後就交由給系統類加載器來完成,這個過程就是委託。這樣作的目的室爲了保證系統類庫加載時的安全性,如:Object、String類等等,這些都應該由引導類加載器來完成,不該該交由其餘類加載器,由於,若是這些類庫加載的後能夠由用戶來進行操做,那麼就可會致使類的不安全。例如:用戶能夠重載或修改類中的方法,這是絕對禁止的。this

委託模式:spa

 

4. ClassLoader的常見API

除了引導到類加載器是有C語言來實現之外,其餘的類加載器都是繼承自ClassLoader這個類,而這個類中提供了相應的加載API來完成類加載以及初解析和始化的過程。對象

API 說明
getParent() 獲取上一級的類加載器
loadClass(String name) 加載名稱爲name的類,返回的是一個Class實例,在此方法中會間接調用findClass方法
findClass(String name) 加載名稱爲name的類,返回的是一個Class實例。一般自定義類加載器時,會重寫此方法。
findLoadedClass(String name) 檢查名稱爲name的Class是否已經加載過,返回的是一個Class實例,若是Class爲null,就表示未加載,不然就是已經加載
defineClass(String name, byte[] b, int off, int len) 將字節數組b的二進制數據轉換成Java中的Class對象
resolveClass(Class c) 解析並鏈接到指定的Class

2. 自定義類加載器

咱們也能夠自定義類加載器,只須要繼承ClassLoader,一般須要重寫父類的findClass方法。須要注意的是,自定義類加載器它的上一級的類加載器是AppClassLoader,也會遵循委託的模式。

示例:

public class DemoClassLoader extends ClassLoader {
    /**
     *  類加載的根路徑
     */
    private String loadRootPath;

    public DemoClassLoader(String loadRootPath){
        this.loadRootPath = loadRootPath;
    }

    /**
     * 重寫父類的findClass方法
     * @param name 須要加載的完整類名
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //構建類文件的絕對路徑,用於I/O流讀寫操做
        String loadPath = loadRootPath + convertNameToPath(name);
        //經過輸入流將class文件讀入內存當中
        File file = new File(loadPath);
        byte[] bytes = readClassFile(file);
        //將直接數組轉換爲Class對象
        Class<?> clazz = defineClass(name, bytes, 0, bytes.length);
        return clazz;
    }

    /**
     * 根據File對象將class文件讀入內存,返回一個字節數組
     * @param file
     * @return
     */
    private byte[] readClassFile(File file){
        //構建文件輸入流
        try(FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream()){
            int len = 0;
            byte[] bytes = new byte[1024];
            while((len = fis.read(bytes, 0, bytes.length)) != -1){
                    //將讀取的直接保存在一個緩存中
                baos.write(bytes, 0, len);
            }
            //將流中的直接數組直接返回
            return baos.toByteArray();
        }catch (IOException e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 將完整類名轉換爲一個相對路徑
     * @return
     */
    private String convertNameToPath(String name){
        String path = name.replace(".", File.separator) + ".class";
        return path;
    }
}

使用:

public class Hello{
	public static void main(String[] argo){
		System.out.print("Hello,World");
	}
}

 

public class Main {
    public static void main(String[] args) throws Exception{
   //測試自定義類加載器
        DemoClassLoader dc = new DemoClassLoader("F:/S3/ClassLoader"+ File.separator);
        Class clazz = dc.loadClass("Hello");
        Object hello = clazz.newInstance();
        Object hello2 = clazz.newInstance();
        //輸出實例化後的Object對象
        System.out.println(hello);
        System.out.println(hello2);
        //輸出加載該類的加載器
        System.out.println(clazz.getClassLoader()); 
  } 
}

輸出:

//表示輸出Class對象
Hello@677327b6
//同一個加載器加載的class文件,產生的Class對象是同樣的
Hello@677327b6
//表示加載這個類的是DemoClassLoader,即自定義的加載器 com.company.DemoClassLoader@74a14482

 

注意:若是是不一樣的類加載器加載同一個class文件,那麼產生的Class對象是不同的。

相關文章
相關標籤/搜索