ClassLoader

ClassLoader教程

1. 簡介

Classloader的核心做用就是將編譯以後的class文件加載到jvm運行的內存當中。在jvm的規範當中,類加載器主要分爲三種:引導類加載器(BootClassLoader)、擴展類加載器(ExtClassLoader)、系統類加載器(AppClassLoader)數組

2. 加載器分類

2. 1引導類加載器

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

2.2 擴展類加載器

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

2.3 系統類加載器

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

3. 委託模式

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

4. ClassLoader的常見API

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

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,也會遵循委託的模式。spa

示例:對象

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;
  }
}

使用:blog

public class Main {
   public static void main(String[] args) throws Exception{
       //獲取當前系統的用戶目錄
       String  rootPath = System.getProperty("user.home")+File.separator;
       DemoClassLoader c1 = new DemoClassLoader(rootPath);
       Class clazz = cl.loadClass("Hello");
    //建立類實例
       Object hello = clazz.newInstance();
       Object hello2 = clazz.newInstance();
       System.out.println(hello);
       System.out.println(hello2);
    //獲取當前Class對象的類加載器
       System.out.println(clazz.getClassLoader());
  }
}

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

相關文章
相關標籤/搜索