Java三個類加載器及它們的相互關系

一、什麼是類加載器?

虛擬機設計團隊把類加載階段中的“通過一個類的全限定名來獲取描述此類的二進制字節流”這個動作放到Java虛擬機外部去實現,以便讓應用程序自己決定如何去獲取所需要的類。實現這個動作的代碼模塊稱為“類加載器”

類加載器可以說是Java語言的一項創新,也是Java語言流行的重要原因之一,它最初是為瞭滿足Java Applet的需求而開發出來的。雖然目前Java Applet技術基本上已經“死掉”,但類加載器卻在類層次劃分、OSGi、熱部署、代碼加密等領域大放異彩,成為瞭Java技術體系中一塊重要的基石,可謂是失之桑榆,收之東隅。

類加載器雖然隻用於實現類的加載動作,但它在Java程序中起到的作用卻遠遠不限於類加載階段。對於任意一個類,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機中的唯一性,每一個類,都擁有一個獨立的類名稱空間。這句話可以表達得更通俗一些:比較兩個類是否“相等”,隻有在這兩個類是由同一個類加載器加載的前提下才有意義。否則,即使這兩個類來源於同一個Class文件,被同一個虛擬機加載,隻要加載它們的類加載器不同,那這兩個類就必定不相等。

二、AppClassLoader系統類加載器

AppClassLoader應用類加載器,又稱為系統類加載器,負責在JVM啟動時,加載來自命令java中的classpath或者java.class.path系統屬性或者CLASSPATH操作系統屬性所指定的JAR類包和類路徑

  • 獲取當前類的類加載器:
public class AppClassLoaderTest {

    public static void main(String[] args) {
        System.out.println(ClassLoader.getSystemClassLoader());
    }

}

輸出:

[email protected]

這說明AppclassLoader是當前應用classpath所有類的加載器。

查看ClassLoader的源碼可發現:在沒有特定說明的情況下,用戶自定義的任何類加載器都將該類加載器作為自定義類加載器的父加載器.

  • 通過執行下面的代碼即可獲得classpath的加載路徑:
String classPath = System.getProperty("java.class.path");
for (String path : classPath.split(";")) {
    System.out.println(path);
}

輸出:

.

通常是當前執行字節碼的路徑。

  • main函數的類的加載就是使用AppClassLoader加載器進行加載的,而AppClassLoader的父加載器是ExtClassLoader:
public class AppClassLoaderTest {

    public static void main(String[] args) {
        ClassLoader classLoader = Test.class.getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());
    }

    private static class Test {

    }

}

輸出:

[email protected] 
[email protected] 

三、ExtClassLoader擴展類加載器

ExtClassLoader稱為擴展類加載器,主要負責加載Java的擴展類庫,默認加載JAVA_HOME/jre/lib/ext/目錄下的所有jar包或者由java.ext.dirs系統屬性指定的jar包.放入這個目錄下的jar包對AppClassLoader加載器都是可見的(因為ExtClassLoader是AppClassLoader的父加載器,並且Java類加載器采用瞭委托機制).

  • ExtClassLoader的類掃描路徑通過執行下面代碼來看一下:
public class ExtClassLoaderTest {
	
	public static void main(String[] args) {
		String extDirs = System.getProperty("java.ext.dirs");
		for (String path : extDirs.split(";")) {
			System.out.println(path);
		}
	}
}

執行結果如下:

D:\TOOLS\JAVA\lib\ext 
C:\WINDOWS\Sun\Java\lib\ext 

  • 從上面的路徑中隨意選擇一個類,來看看它的類加載器是什麼:
public class ExtClassLoaderTest {
	
	public static void main(String[] args) {
		ClassLoader classLoader = sun.security.ec.SunEC.class.getClassLoader();
		System.out.println(classLoader);
		System.out.println(classLoader.getParent());
	}
}

輸出:

[email protected] 
null 

從輸出結果可知ExtClassLoader的父加載器為null

四、BootstrapClassLoader啟動類加載器

啟動類加載器,是Java類加載層次中最頂層的類加載器,負責加載JDK中的核心類庫,如:rt.jar、resources.jar、charsets.jar等

  • 通過如下程序獲得該類加載器從哪些地方加載瞭相關的jar或class文件:
import java.net.URL;

public class BootstraplassLoaderPath {
	
	public static void main(String[] args) {
		URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
		for (URL url : urLs) {
    	System.out.println(url.toExternalForm());
		}
	}
}

輸出:

file:/D:/TOOLS/JAVA/lib/resources.jar
file:/D:/TOOLS/JAVA/lib/rt.jar
file:/D:/TOOLS/JAVA/lib/sunrsasign.jar
file:/D:/TOOLS/JAVA/lib/jsse.jar
file:/D:/TOOLS/JAVA/lib/jce.jar
file:/D:/TOOLS/JAVA/lib/charsets.jar
file:/D:/TOOLS/JAVA/lib/jfr.jar
file:/D:/TOOLS/JAVA/classes

  • 從rt.jar中選擇String類,看一下String類的類加載器是什麼:
public class stringBootstraplassLoaderTest {
	
	public static void main(String[] args) {
		ClassLoader classLoader = String.class.getClassLoader();
    System.out.println(classLoader);
	}
}

輸出:

null

由於BootstrapClassLoader對Java不可見,所以返回瞭null,我們也可以通過某一個類的加載器是否為null來作為判斷該類是不是使用BootstrapClassLoader進行加載的依據。
另外上面提到ExtClassLoader的父加載器返回的是null,那是否說明ExtClassLoader的父加載器是BootstrapClassLoader.

Bootstrap ClassLoader是由C/C++編寫的,它本身是虛擬機的一部分,所以它並不是一個JAVA類,也就是無法在java代碼中獲取它的引用,JVM啟動時通過Bootstrap類加載器加載rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加載。

五、加載器關系總結

JVM初始化sun.misc.Launcher並創建Extension ClassLoader和AppClassLoader實例。並將ExtClassLoader設置為AppClassLoader的父加載器,Bootstrap則是ExtClassLoader的父加載器.
關系圖如下:

六、參考資料

1.Java類加載器
2.一看你就懂,超詳細java中的ClassLoader詳解

到此這篇關於Java三個類加載器及它們的相互關系的文章就介紹到這瞭,更多相關Java類加載器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!