一、ClassLoader的概念
Java程式在執行的時候,JVM通過類載入機制(ClassLoader)把class檔案載入到記憶體中,只有class檔案被載入記憶體,才能被其他class引用,使程式正確執行起來.
二、ClassLoader的分類
Java中的ClassLoader有三種.
1. Bootstrap ClassLoader
由C++寫的,由JVM啟動.
啟動類載入器,負責載入java基礎類,對應的檔案是%JRE_HOME/lib/ 目錄下的rt.jar、resources.jar、charsets.jar和class等
2.Extension ClassLoader
Java類,繼承自URLClassLoader
擴充套件類載入器,對應的檔案是 %JRE_HOME/lib/ext 目錄下的jar和class等
3.App ClassLoader
Java類,繼承自URLClassLoader
系統類載入器,對應的檔案是應用程式classpath目錄下的所有jar和class等
三、ClassLoader的過程
1.三者關係
Java的類載入使用雙親委託機制來搜尋類.三種ClassLoader存在父子關係,App ClassLoader的父類載入器是Extension ClassLoader,Extension ClassLoader的父類載入器是Bootstrap ClassLoader,要注意的一點是,這裡的父子並不是繼承關係.
我新建一個類Test來驗證三者的關係.
public class Test { public static void main(String[] args) { ClassLoader ClassLoader1 = Test.class.getClassLoader(); ClassLoader ClassLoader2 = ClassLoader1.getParent(); ClassLoader ClassLoader3 = ClassLoader2.getParent(); System.out.println(ClassLoader1); System.out.println(ClassLoader2); System.out.println(ClassLoader3); }}
看下輸出結果
[email protected]$ExtClassLoader@1ff0ddenull
2.載入機制
當這三者中的某個ClassLoader要載入一個類時,會先委託它的父類載入器嘗試載入,一直往上,如果最頂級的父類載入器沒有找到該類,那麼委託者則親自到特定的地方載入,如果沒找到,那麼就丟擲異常ClassNotFoundException.這裡畫張圖來表示下
這裡要注意一點:只有被同一個類載入器例項載入並且檔名相同的class檔案才被認為是同一個class.
四、自定義ClassLoader
1.為什麼要自定義ClassLoader
因為系統的ClassLoader只會載入指定目錄下的class檔案,如果你想載入自己的class檔案,那麼就可以自定義一個ClassLoader.
2.如何自定義ClassLoader
2.1
新建一個類繼承自java.lang.ClassLoader,重寫它的findClass方法。
2.2
將class位元組碼陣列轉換為Class類的例項
2.3
呼叫loadClass方法即可
3.例子
我先建一個叫Log的類,很簡單,只有一句列印
public class Log { public static void main(String[] args) { System.out.println("載入成功"); }}
我把這個java檔案放到D盤根目錄,然後開啟cmd,用javac命令把java檔案轉化為class檔案
然後我新建一個MyClassLoader繼承自ClassLoader
public class MyClassLoader extends ClassLoader { @Override protected Class findClass(String name) throws ClassNotFoundException { Class log = null; // 獲取該class檔案位元組碼陣列 byte[] classData = getData(); if (classData != null) { // 將class的位元組碼陣列轉換成Class類的例項 log = defineClass(name, classData, 0, classData.length); } return log; } private byte[] getData() { //指定路徑 String path = "D:/Log.class"; File file = new File(path); FileInputStream in = null; ByteArrayOutputStream out = null; try { in = new FileInputStream(file); out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int size = 0; while ((size = in.read(buffer)) != -1) { out.write(buffer, 0, size); } } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } return out.toByteArray(); }}
最後測試一下,輸出載入這個Log的class檔案的載入器,並且利用反射呼叫它的mian方法.
public class Test { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { MyClassLoader myClassLoader = new MyClassLoader(); //查詢Log這個class檔案 myClassLoader.findClass("Log"); //載入Log這個class檔案 Class Log = myClassLoader.loadClass("Log"); System.out.println("類載入器是:"+Log.getClassLoader()); //利用反射獲取main方法 Method method=Log.getDeclaredMethod("main", String[].class) ; Object object=Log.newInstance(); String [] arg={"ad"}; method.invoke(object, (Object)arg); }}