(點選上方公眾號,可快速關註)
來源:五月的倉頡 ,
www.cnblogs.com/xrq730/p/4845144.html
類與類載入器
虛擬機器設計團隊把類載入階段張的”透過一個類的全限定名來獲取此類的二進位制位元組流”這個動作放到Java虛擬機器外部去實現,以便讓應用程式自己決定如何去獲取所需要的類。實現這個動作的程式碼模組稱為”類載入器”。類載入器雖然只用於實現類的載入動作,但它在Java程式中起到的作用卻遠遠不限定於類載入階段。對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。這句話表達地再簡單一點就是:比較兩個類是否”相等”,只有在這兩個類是由同一個類載入器載入的前提下才有意義,否則即使這兩個類來源於同一個.class檔案,被同一個虛擬機器載入,只要載入它們的類載入器不同,這兩個類必定不相等。
上面說的”相等”,包括代表類的.class物件的equals()方法、isAssignableFrom()方法、isInstance()方法的傳回結果,也包括使用instanceof關鍵字做物件所屬關係判定等情況。
類載入器模型
從Java虛擬機器的角度講,只有兩種不同的類載入器:啟動類載入器Bootstrap ClassLoader,這個類載入器是由C++語言實現的,是虛擬機器自身的一部分;其他類載入器,這些類載入器都由Java語言實現,獨立於虛擬機器外部,並且全部繼承自java.lang.ClassLoader。從開發人員的角度講,類載入器還可以劃分地更加細緻一些,一張圖就能說明:
關於這張圖首先說兩點:
-
這三個層次的類載入器並不是繼承關係,而只是層次上的定義
-
它並不是一個強制性的約束模型,而是Java設計者推薦給開發者的一種類載入器實現方式
OK,然後一個一個類載入器來看:
1、啟動類載入器Bootstrap ClassLoader
之前說過了這是一個嵌在JVM核心中的載入器。它負責載入的是JAVA_HOME/lib下的類庫,系統類載入器無法被Java程式直接應用
2、擴充套件類載入器Extension ClassLoader
這個類載入器由sun.misc.Launcher$ExtClassLoader實現,它負責用於載入JAVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統變數指定所指定的路徑中所有類庫,開發者可以直接使用擴充套件類載入器。java.ext.dirs系統變數所指定的路徑的可以透過程式來檢視
public class TestMain
{
public static void main(String[] args)
{
System.out.println(System.getProperty(“java.ext.dirs”));
}
}
執行結果
E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
3、應用程式類載入器Application ClassLoader
這個類載入器由sun.misc.Launcher$AppClassLoader實現。這個類也一般被稱為系統類載入器,寫個小程式看下:
public class TestMain
{
public static void main(String[] args)
{
System.out.println(ClassLoader.getSystemClassLoader());
}
}
執行結果為:
sun.misc.Launcher$AppClassLoader@546b97fd
看到透過”ClassLoader.getSystemClassLoader”,得到的是sun.misc.Launcher$AppClassLoader,這也證明瞭JDK認為Application ClassLoader是系統類載入器。順便根據類載入器模型,列印一下這個類的父載入器:
public class TestMain
{
public static void main(String[] args)
{
System.out.println(ClassLoader.getSystemClassLoader().getParent());
}
}
執行結果為:
sun.misc.Launcher$ExtClassLoader@535ff48b
看出Application ClassLoader的父載入器確實是Extension ClassLoader,符合圖中的模型。那麼再列印父載入器呢?按照我們的想法應該是Bootstrap ClassLoader了,看下是不是:
public class TestMain
{
public static void main(String[] args)
{
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
}
}
執行結果為:
null
這會打印出來的是null了。其實也很好理解,Bootstrap ClassLoader以外的ClassLoader都是Java實現的,因此這些ClassLoader勢必在Java堆中有一份實體在,所以Extension ClassLoader和Application ClassLoader都能打印出內容來。但是Bootstrap ClassLoader是JVM的一部分,是用C/C++寫的,不屬於Java,自然在Java堆中也沒有自己的空間,所以就傳回null了。所以,如果ClassLoader得到的是null,那麼表示的ClassLoader就是Bootstrap ClassLoader。
另外要說很重要的一點,反編譯一下rt.jar,找到sun.misc.Launcher看一下Application ClassLoader的實現:
static class AppClassLoader extends URLClassLoader
{
public static ClassLoader getAppClassLoader(final ClassLoader paramClassLoader)
throws IOException
{
String str = System.getProperty(“java.class.path”);
final File[] arrayOfFile = str == null ? new File[0] : Launcher.getClassPath(str);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction()
{
public Launcher.AppClassLoader run()
{
URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.pathToURLs(arrayOfFile);
return new Launcher.AppClassLoader(arrayOfURL, paramClassLoader);
}
});
}
重點就在第6行,Application ClassLoader只會載入java.class.path下的.class檔案,java.class.path代表的是什麼路徑?列印一下:
public class TestMain
{
public static void main(String[] args)
{
System.out.println(System.getProperty(“java.class.path”));
}
}
執行結果為:
F:\程式碼\MyEclipse\TestArticle\bin;F:\學習\第三方jar包\XStream\xstream-1.4.jar;F:\學習\第三方jar包\XStream\kxml2.jar
我這裡有新增兩個.jar到CLASSPATH下。那也可以下一個重要的結論了:
Application ClassLoader只能載入專案bin目錄下的.class檔案。
雙親委派模型
最後講一下雙親委派模型,其實上面的類載入器模型圖就是一個雙親委派樣式的圖,這裡把它再講清楚一點。
雙親委派模型是在JDK1.2期間被引入的,其工作過程可以分為兩步:
1、如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器去完成,每一個層次的類載入器都是如此。
2、只有當父載入器反饋自己無法完成這這個載入請求(它的搜尋範圍中沒有找到所需的類)時,子載入器才會嘗試自己去載入
所以,其實所有的載入請求最終都應該傳送到頂層的啟動類載入器中。雙親委派模型對於Java程式的穩定運作很重要,因為Java類隨著它的載入器一起具備了一種帶有優先順序的層次關係。例如java.lang.Object,存放於rt.jar中,無論哪一個類載入器要去載入這個類,最終都是由Bootstrap ClassLoader去載入,因此Object類在程式的各種類載入器環境中都是一個類。相反,如果沒有雙親委派模型,由各個類自己去載入的話,如果使用者自己編寫了一個java.lang.Object,並放在CLASSPATH下,那系統中將會出現多個不同的Object類,Java體系中最基礎的行為也將無法保證,應用程式也將會變得一片混亂。
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能