天天看點

Java類加載器及雙親委派

文章對應java8 Java中類加載器有以下三種:

1、啟動類加載器(根類加載器Bootstrap ClassLoader)

2、擴充類加載器(ExtClassLoader)

3、應用類加載器(AppClassLoader)

各加載器負責加載的類:

Bootstrap ClassLoader:負責加載JAVA_HOME下lib目錄下的類

ExtClassLoader:負責加載JAVA_HOME下lib\ext目錄下的類

AppClassLoader:負責加載使用者類路徑所指定的類庫中的類

各加載器實作:

啟動類加載器:從名字上看就能确定這不是一個java類,事實上底層是由C++實作的,是以啟動類加載器在java程式中無法直接引用。

擴充類加載器:由sun.misc.Launcher$ExtClassLoader實作

應用類加載器:由sun.misc.Launcher$AppClassLoader實作,可由ClassLoader中的getSystemClassLoader方法傳回,是以也叫系統類加載器,在java類未指定加載器時的預設加載器,也是典型的用于啟動應用的類加載器

加載器怎麼産生:

從上邊實作上我們可以看出,對于啟動類加載器,是由非Java的底層代碼在JVM啟動時生成的。對于擴充類和應用類,都是通過sun.misc.Launcher類來生成。

public Launcher() {
  ...
  var1 = Launcher.ExtClassLoader.getExtClassLoader();
  ...
  this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
  ...
  Thread.currentThread().setContextClassLoader(this.loader);
  ...
}
           
雙親委派

具體來講,就是類加載器在收到一個類的加載請求時,會先委托給父類加載器加載,依次遞歸,如果父類無法完成加載,子類加載器才會嘗試加載。通俗的說,兒子懶,有事了先讓父親做,父親做不了自己才做。

我們主要應該看看為什麼要這樣?

在前邊的文章中也提到過自定義類加載器,為什麼會需要自定義類加載器?因為有的類可能需要通過網絡傳輸、加密、編解碼等操作,在這樣的情況下就必須使用自定義類加載器來完成加載任務。

然後我們再看看這和雙親委派有什麼關系?

設想一下,如果不是這樣一種雙親委派的關系,那麼核心類庫JAVA_HOME下lib目錄下的類由啟動類加載器加載,如果我們自己也寫了一個java.lang.System類,這個類會被系統類進行加載,這造成了什麼?

1、重複加載(核心類庫中的System已加載)

2、核心類API被破壞,虛拟機安全無法保證

然後我們可以總結出如下結論:

如果使用雙親委派加載機制,就能夠確定類加載器和類(全限定名)一起構成了一種類似帶有優先級的層次關系

1)被加載之後就不會再被重複加載

2)確定核心類庫中相應的API不會被篡改,核心功能及JVM安全能夠得到保證

即使你從網絡或者其他地方傳來一個java.lang.System類,虛拟機通過雙親委派機制發現其加載類為啟動類,并已被加載,就不再觸發加載動作

上下文類加載器

其作用就是用于類的逆向通路而提出的一種概念,本質上還是應用類加載器。(類的逆向通路的典型例子就是JDBC加載,父類加載器加載的類需要通路子類加載器加載的類)