標籤:tput 建立 puts strong 使用者建立 err pcl turn sync
概述
ClassLoader的雙親委託模式:classloader 按層級分為三個層級:最上級 : bootstrap classLoader(根類載入器) ; 中間級:extension classLoader (擴充類載入器) 最低級 app classLoader(應用類載入器)。
根(Bootstrap)類載入器:該載入器沒有父載入器。它負責載入虛擬機器的核心類庫,如java.lang.*等。例如java.lang.Object就是由根類載入器載入的。根類載入器從系統屬性sun.boot.class.path所指定的目錄中載入類庫。根類載入器的實現依賴於底層作業系統,屬於虛擬機器的實現的一部分,它並沒有繼承java.lang.ClassLoader類。
擴充(Extension)類載入器:它的父載入器為根類載入器。它從java.ext.dirs系統屬性所指定的目錄中載入類庫,或者從JDK的安裝目錄的jre/lib/ext子目錄(擴充目錄)下載入類庫,如果把使用者建立的JAR檔案放在這個目錄下,也會自動由擴充類載入器載入。擴充類載入器是純Java類,是java.lang.ClassLoader類的子類。
系統(System)類載入器:也稱為應用類載入器,它的父載入器為擴充類載入器。它從環境變數classpath或者系統屬性java.class.path所指定的目錄中載入類,它是使用者自訂的類載入器的預設父載入器。系統類別載入器是純Java類,是java.lang.ClassLoader類的子類。
父子載入器並非繼承關係,也就是說子載入器不一定是繼承了父載入器。
對於Java來說,java 虛擬機器要將被用到的java類檔案通過classLoader 載入到JVM記憶體中,而這個classloader就是bootstrap classloader。而對於APP而言,首先請求app 級來載入,繼而請求extension classLoader,最後啟動bootstrap classLoader 。
自訂ClassLoader
public class MyClassLoader extends ClassLoader { //類載入器名稱 private String name; //載入類的路徑 private String path = "D:/"; private final String fileType = ".class"; public MyClassLoader(String name){ //讓系統類別載入器成為該 類載入器的父載入器 super(); this.name = name; } public MyClassLoader(ClassLoader parent, String name){ //顯示指定該類載入器的父載入器 super(parent); this.name = name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } @Override public String toString() { return this.name; } private byte[] loaderClassData(String name){ InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); this.name = this.name.replace(".", "/"); try { is = new FileInputStream(new File(path + name + fileType)); int c = 0; while(-1 != (c = is.read())){ baos.write(c); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally{ try { is.close(); baos.close(); } catch (IOException e) { e.printStackTrace(); } } return data; } @Override public Class<?> findClass(String name){ byte[] data = loaderClassData(name); return this.defineClass(name, data, 0, data.length); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //loader1的父載入器為系統類別載入器 MyClassLoader loader1 = new MyClassLoader("loader1"); loader1.setPath("D:/lib1/"); //loader2的父載入器為loader1 MyClassLoader loader2 = new MyClassLoader(loader1, "loader2"); loader2.setPath("D:/lib2/"); //loader3的父載入器為根類載入器 MyClassLoader loader3 = new MyClassLoader(null, "loader3"); loader3.setPath("D:/lib3/"); Class clazz = loader2.loadClass("Sample"); Object object = clazz.newInstance(); }}public class Sample { public Sample(){ System.out.println("Sample is loaded by " + this.getClass().getClassLoader()); new A(); }}public class A { public A(){ System.out.println("A is loaded by " + this.getClass().getClassLoader()); }}
每一個自訂ClassLoader都必須繼承ClassLoader這個抽象類別,而每個ClassLoader都會有一個parent ClassLoader,我們可以看一下ClassLoader這個抽象類別中有一個getParent()方法,這個方法用來返回當前 ClassLoader的parent,注意,這個parent不是指的被繼承的類,而是在執行個體化該ClassLoader時指定的一個 ClassLoader,如果這個parent為null,那麼就預設該ClassLoader的parent是bootstrap classloade。
上面講解了一下ClassLoader的作用以及一個最基本的載入流程,接下來我們說說ClassLoader使用了雙親委託模式進行類載入。
ClassLoader雙親委託模式
通俗的講,就是某個特定的類載入器在接到載入類的請求時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務時,才自己去載入。
為了更好的理解雙親委託模式,我們先自訂一個ClassLoader,假設我們使用這個自訂的ClassLoader載入 java.lang.String,那麼這裡String是否會被這個ClassLoader載入呢?
事實上java.lang.String這個類並不會被我們自訂的classloader載入,而是由bootstrap classloader進行載入,為什麼會這樣?實際上這就是雙親委託模式的原因,因為在任何一個自訂ClassLoader載入一個類之前,它都會先 委託它的父親ClassLoader進行載入,只有當父親ClassLoader無法載入成功後,才會由自己載入。而在上面的例子中,因為 java.lang.String是屬於java核心API的一個類,所以當使用自訂的classloader載入它的時候,該 ClassLoader會先委託它的父親ClassLoader進行載入(bootstrap classloader),所以並不會被我們自訂的ClassLoader載入。
我們來看一下ClassLoader的一段源碼:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ // 首先檢查該name指定的class是否有被載入 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //如果parent不為null,則調用parent的loadClass進行載入 c = parent.loadClass(name, false); }else{ //parent為null,則調用BootstrapClassLoader進行載入 c = findBootstrapClass0(name); } }catch(ClassNotFoundException e) { //如果仍然無法載入成功,則調用自身的findClass進行載入 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
使用雙親委託模式優點
那麼我們使用雙親委託模式有什麼好處呢?
- 因為這樣可以避免重複載入,當父親已經載入了該類的時候,就沒有必要子ClassLoader再載入一次。
- 考慮到安全因素,我們試想一下,如果不使用這種委託模式,那我們就可以隨時使用自訂的String來動態替代java核心api中定義類型,這樣會存在非常大的安全隱患,而雙親委託的方式,就可以避免這種情況,因為String已經在啟動時被載入,所以使用者自訂類是無法載入一個自訂的ClassLoader。
附:Android ClassLoader簡介
android classloader雙親委託模式