天天看點

第二篇 JVM之類加載器

在類加載過程的加載階段,有一個通過類的全限定名擷取描述類的二進制流的動作,Java虛拟機設計團隊有意将這個動作的實作放到虛拟機之外實作,以便讓使用者自己決定如何擷取所需類,這個動作的實作被稱為類加載器。

  在類加載過程的加載階段,有一個通過類的全限定名擷取描述類的二進制流的動作,Java虛拟機設計團隊有意将這個動作的實作放到虛拟機之外實作,以便讓使用者自己決定如何擷取所需類,這個動作的實作被稱為類加載器。JVM根據職能的不同,設計了以下四種類加載器:

1、引導類加載器(BootStrap ClassLoader)

2、擴充類加載器(Extension ClassLoader)

3、應用程式類加載器(Application ClassLoader)

4、自定義類加載器(User ClassLoader)

  前三種是虛拟機自帶的類加載器,自定義類加載器是使用者根據自己的需求設計的類加載器,下圖是四種類加載器之間的關系,圖中所表示的層次關系,并非類的繼承關系,而是描述類加載器協作關系,通常這個協作關系通過組合的方式實作(設計模式中的組合,通過組合複用父類加載器的代碼,除了引導類加載器,其他類加載器都有父類加載器),這個協作動作的實作被稱為"雙親委派模型",後面的篇章中單獨講。

第二篇 JVM之類加載器

在Java程式中可按照如下代碼擷取除引導類加載器外的各個類加載器。

  引導類加載器是由C/C++語言實作,嵌套在虛拟機内部,用來加載java核心庫中的類,特性如下:

1、隻加載JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路徑下的内容

2、加載擴充類和應用程式類類加載器,并且是它們的父類加載器,不繼承ClassLoader類,其本身沒有父類加載器。

3、出于安全考慮,隻加載包名為java、javax、sun開頭的類,

4、引導類加載器無法通過getClassLoader()方法擷取,隻會傳回null,如String.class.getClassLoader()。

  擴充類加載器是Java語言實作的類加載器,在sun.misc.Launcher.ExtClassLoader中實作,派生于ClassLoader類,特性:

1、本身先有引導類加載器加載,父類加載器為引導類加載器(非繼承關系)。

2、加載java.ext.dirs系統屬性所指定的目錄中的類庫,或者加載JDK安裝目錄的jre/ext/dir擴充目錄下的類庫,使用者可以将自己的jar放入該目錄,通過擴充類加載器加載。

  應用程式類加載器也是Java語言實作的類加載器,在sun.misc.Launcher.AppClassLoader中實作,派生于ClassLoader類,特性:

1、本身先有引導類加載器加載,父類加載器為擴充類加載器(非繼承關系)。

2、加載環境變量classpath或者java.class.path屬性指定的目錄下的類庫。

3、程式中預設的類加載器,一般的Java應用程式都由它加載,可以通過java.lang.ClassLoader#getSystemClassLoader方法擷取。

  對于某一些特殊的類加載需求,使用者可以通過繼承ClassLoader實作自定義的類加載器,通過自定義類加載器,可以在以下的需求場景使用:

1、隔離類,如類路徑沖突。

2、防反編譯加密Class檔案。

3、擴充類的加載源。

  自定義實作類加載器可以通過繼承java.lang.ClassLoader并重寫loadClas()方法或者實作findClass()(推薦)方法,如下代碼就是加載指定目錄class的簡單實作。

  在虛拟機中,每個類的唯一性,由類和類加載器兩個因素決定,也就是即使同一個類被不同類加載器加載,也會導緻這兩個類不相同。在上篇文章說過,類加載完成以後會在堆區生成一個類的java.lang.Class對象作為外部接口通路方法區對應的類資訊,這個操作具象化就是Object的getClass()方法。getClass()擷取到類的Class對象,進而根據getClassLoader()方法擷取類加載器,下面代碼中自定義了類加載器MyClassLoader,在main中通過自定義類加載器加載一次ClassLoaderUniqueTest類并建立執行個體,同時在執行main方法時,虛拟機會通過應用程式類加載器加載一次ClassLoaderUniqueTest類,最後通過instanceof比較類型,這個比較類型并不限于instanceof,還可以用equals。

執行結果