-----------------------------------------------------------------------
虛擬機類加載機制
-----------------------------------------------------------------------
1,類與類加載器的關係
一個類需要由加載它的類加載器和這個類本身一同來確定這個類在Java虛擬機中的唯一性。
注:每一個類加載器,都有一個獨立的類命名空間。
判斷兩個類是否相等:
只有當兩個類是由同一個類加載器加載時,判斷它們是否相等纔有意義。
如果兩個類不是由同一個類加載器加載的,即使這兩個類來源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不同,那麼這兩個類一定不相等。
2,雙親委派模型
要求:除了頂層的啓動類加載器外,其餘的類加載器都有自己的父加載器。
注意:類加載器間的父子關係不是以繼承的方式實現的,而是以組合關係的方式來複用父加載器的代碼。
原理: 如果一個類加載器收到了類加載的請求,它首先會把這個請求委派給父加載器去完成,每一個層次的類加載器都是如此。
因此,所有的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(在它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試自己去加載。
好處:Java類型體系中最基礎的行爲得到了保證,從而保證了Java程序的穩定運行。
Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係。
3,系統提供的3種類加載器
啓動類加載器(Bootstrap ClassLoader):負責將存放在 %JAVA_HOME%/lib 目錄中的類庫(如:rt.jar)加載到虛擬機內存中,用來加載Java的核心API,由本地代碼(C)編寫。
擴展類加載器(Extension ClassLoader):負責加載%JAVA_HOME%/lib/ext 目錄中的類庫,用來加載Java的擴展API
源碼:rt.jar中的sun.misc.Launcher.ExtClassLoader類。
應用程序類加載器(Application ClassLoader):負責加載用戶類路徑(ClassPath)中的類庫。
源碼:rt.jar中的sun.misc.Launcher.AppClassLoader類。
注:AppClassLoader是java.lang.ClassLoader中getSystemClassLoader()方法的返回值。故又稱之爲:系統類加載器
4,ClassLoader加載流程
當運行一個程序時,JVM啓動,運行Bootstrap ClassLoader,該加載器加載了Java核心的API(Extension ClassLoader和Application ClassLoader也在此時被加載),
然後調用Extension ClassLoader加載擴展API,最後Application ClassLoader加載classpath下定義的Class
5,Class.forName和classloader
目的:ClassLoader.loadClass()方法和Class.forName()方法的目的都是用來加載class的
區別:loadClass()方法在加載類的的時候並不對該類進行解析,因此不會初始化該類;而forName()方法在加載類的時候會將類進行解析和初始化。
ClassLoader.loadClass()方法
protected Class<?> loadClass(String name, boolean resolve)
name - 類的二進制名稱
resolve - 是否解析這個類,默認爲false
public Class<?> loadClass(String name)相當於:loadClass(name, false)
Class.forName()方法
public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
name - 所需類的完全限定名
initialize - 是否必須初始化類,默認爲true
loader - 用於加載類的類加載器,默認爲當前類的類加載器
public static Class<?> forName(String className)相當於:forName(className, true, currentLoader)
6,類加載的過程:裝載、連接、初始化
其中:連接分爲三步:驗證、準備、解析
裝載:找到相應的Class文件,讀入JVM
連接:
1)驗證:驗證Class文件是否符合規定,確保Class文件中包含的信息符合當前虛擬機的要求。
2)準備:爲類變量分配內存並設置類變量的默認初始值,這些變量所使用的內存都將在方法區中進行分配。
注意:此時進行內存分配的只有類變量(static變量),不包括實例變量。實例變量會在對象實例化時隨着對象一起分配在Java堆中。
3)解析:將常量池中的符合引用替換爲直接引用。
初始化:類的初始化。
7,類初始化的時機:
1)使用new關鍵字來實例化對象的時候、讀取或設置類的靜態字段(注:被final修飾、已在編譯期間把結果放入常量池的靜態字段除外),以及調用類的靜態方法時。
注:子類引用父類的靜態字段,只會觸發父類的初始化而不會觸發子類的初始化。
2)使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化
3)當初始化一個類的時候,如果發現其父類還沒有初始化,則需要先觸發其父類的初始化
4)當虛擬機啓動時,用戶需要指定一個要執行的主類(包含main方法的類),虛擬機會先初始化這個主類
8,類的初始化順序
初始化順序依次是(靜態變量、靜態初始化塊)>(變量、初始化塊)> 構造器
*對於靜態變量和靜態初始化塊之間、變量和初始化塊之間的先後順序:依照他們在類中的聲明順序進行初始化的
有繼承關係時:(父類的靜態變量、靜態初始化塊)>(子類的靜態變量、靜態初始化塊)>(父類變量、初始化塊)>(父類的構造器)>(子類變量、初始化塊)>(子類構造器)
*並不是父類完全初始化完畢後才進行子類的初始化,子類的靜態變量和靜態初始化塊的初始化是在父類的變量、初始化塊和構造器初始化之前就完成了
9,查看jar包中是否包含某個類: jar -tf test.jar | grep 類名
====