Classloader的核心做用就是將編譯以後的class文件加載到jvm運行的內存當中。在jvm的規範當中,類加載器主要分爲三種:java
引導類加載器(BootClassLoader)數組
擴展類加載器(ExtClassLoader)緩存
系統類加載器(AppClassLoader)安全
引導類加載器主要加載的class是jdk自己的類庫(JAVA_HOME\lib目錄下的jar文件),這些類庫都是jdk核心的類庫(rt.jar),也是最重要的,所以由這個加載器去完成加載和初始化。而且這個加載器是使用C語言實現的。jvm
擴展類加載器的主要租用就是加載jdk的擴展包的jar文件(JAVA_HOME\lib\ext目下的jar文件),這些文件是JDK的擴展類庫。ide
系統了加載器的核心做用就是加載classpath路徑下的class文件以及jar文件,這些類庫一般由開發人員本身編寫的,所以這些類都是經過系統類加載完成加載。測試
在執行類加載的時候,最早由系統類加載器開始,但它會把加載的執行權先交給擴展類加載器,一樣,擴展類加載器也會將加載的執行權交給引導類加載器,最終由引導類加載器開始加載,若是須要加載的類庫不是引導類加載器加載的範疇,那麼就會將加載權交回給擴展類加載器。一樣,若是類庫不是擴展類加載器加載的範疇,最後就交由給系統類加載器來完成,這個過程就是委託。這樣作的目的室爲了保證系統類庫加載時的安全性,如:Object、String類等等,這些都應該由引導類加載器來完成,不該該交由其餘類加載器,由於,若是這些類庫加載的後能夠由用戶來進行操做,那麼就可會致使類的不安全。例如:用戶能夠重載或修改類中的方法,這是絕對禁止的。this
委託模式:spa
除了引導到類加載器是有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 |
咱們也能夠自定義類加載器,只須要繼承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對象是不同的。