1、Java的類加載器的種類都有哪些?
● 根類加載器(Bootstrap)--C++寫的,看不到源碼;
● 擴展類加載器(Extension)--加載位置:jre\lib\ext中;
● 系統(應用)類加載器(System\App) --加載位置:classpath中;
● 自定義加載器(必須繼承ClassLoader)。
● 創建類的實例,也就是new一個對象
● 訪問某個類或接口的靜態變量,或者對該靜態變量賦值
● 調用類的靜態方法
● 反射(Class.forName("com.lyj.load"))
● 初始化一個類的子類(會首先初始化子類的父類)
● JVM啟動時標明的啟動類,即文件名和類名相同的那個類只有這6中情況才會導致類的類的初始化。
● 類的初始化步驟:
如果這個類還沒有被加載和鏈接,那先進行加載和鏈接;
假如這個類存在直接父類,并且這個類還沒有被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用于接口);
加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。
java是一種類型安全的語言,它有四類稱為安全沙箱機制的安全機制來保證語言的安全性,這四類安全沙箱分別是:
1.類加載體系
2.class文件檢驗器
3.內置于Java虛擬機(及語言)的安全特性
4.安全管理器及Java API
● 主要講解類的加載體系:
java程序中的.java文件編譯完會生成.class文件,而.class文件就是通過被稱為類加載器的ClassLoader加載的,而ClassLoder在加載過程中會使用“雙親委派機制”來加載.class文件,圖:
BootStrapClassLoader:啟動類加載器,該ClassLoader是jvm在啟動時創建的,用于加載$JAVA_HOME$/jre/lib下面的類庫(或者通過參數-Xbootclasspath指定)。由于啟動類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啟動類加載器的引用,所以不能直接通過引用進行操作。
ExtClassLoader:擴展類加載器,該ClassLoader是在sun.misc.Launcher里作為一個內部類ExtClassLoader定義的(即sun.misc.Launcher$ExtClassLoader),ExtClassLoader會加載$JAVA_HOME/jre/lib/ext下的類庫(或者通過參數-Djava.ext.dirs指定)。
AppClassLoader:應用程序類加載器,該ClassLoader同樣是在sun.misc.Launcher里作為一個內部類,AppClassLoader定義的(即sun.misc.Launcher$AppClassLoader),AppClassLoader會加載java環境變量CLASSPATH所指定的路徑下的類庫,而CLASSPATH所指定的路徑可以通過System.getProperty("java.class.path")獲取;當然,該變量也可以覆蓋,可以使用參數-cp,例如:java-cp路徑(可以指定要執行的class目錄)。
CustomClassLoader:自定義類加載器,該ClassLoader是指我們自定義的ClassLoader,比如tomcat的StandardClassLoader屬于這一類;當然,大部分情況下使用AppClassLoader就足夠了。
前面談到了ClassLoader的幾類加載器,而ClassLoader使用雙親委派機制來加載class文件的。ClassLoader的雙親委派機制是這樣的(這里先忽略掉自定義類加載器CustomClassLoader):
1.當AppClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成。
2.當ExtClassLoader加載一個class時,它首先也不會自己去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。
3.如果BootStrapClassLoader加載失敗(例如在$JAVA_HOME$/jre/lib里未查找到該class),會使用ExtClassLoader來嘗試加載;
4.若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,如果AppClassLoader也加載失敗,則會報出異常ClassNotFoundException。
下面貼下ClassLoader的loadClass(String name,boolean resolve)的源碼:
protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
// 首先找緩存是否有 class
Class c=findLoadedClass(name);
if(c==null){
//沒有判斷有沒有父類
try{
if(parent!=null){
//有的話,用父類遞歸獲取 class
c=parent.loadClass(name,false);
}else{
//沒有父類。通過這個方法來加載
c=findBootstrapClassOrNull(name);}
}catch(ClassNotFoundException e){
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if(c==null){
// 如果還是沒有找到,調用 findClass(name)去找這個類
c=findClass(name);}
}
if(resolve){
resolveClass(c);}
return c;
}
代碼很明朗:
首先找緩存(findLoadedClass),沒有的話就判斷有沒有parent,有的話就用parent來遞歸的loadClass,然而ExtClassLoader并沒有設置parent,則會通過findBootstrapClassOrNull來加載class,而findBootstrapClassOrNull則會通過JNI方法”private native Class findBootstrapClass(String name)”來使用BootStrapClassLoader來加載class。
如果parent未找到class,則會調用findClass來加載class,findClass是一個protected的空方法,可以覆蓋它以便自定義class加載過程。另外,雖然ClassLoader加載類是使用loadClass方法,但是鼓勵用ClassLoader的子類重寫findClass(String),而不是重寫loadClass,這樣就不會覆蓋了類加載默認的雙親委派機制。
雙親委派托機制為什么安全:
舉個例子,ClassLoader加載的class文件來源很多,比如編譯器編譯生成的class、或者網絡下載的字節碼。而一些來源的class文件是不可靠的,比如我可以自定義一個java.lang.Integer類來覆蓋jdk中默認的Integer類,例如下面這樣:
package java.lang;
public class Integer {
public Integer(int value) {
System.exit(0);
}
}
初始化這個Integer的構造器是會退出JVM,破壞應用程序的正常進行,如果使用雙親委派機制的話該Integer類永遠不會被調用,以為委托BootStrapClassLoader加載后會加載JDK中的Integer類而不會加載自定義的這個,可以看下下面這測試個用例:
public static void main(String...args){
Integer i=new Integer(1);
System.err.println(i);
}
執行時JVM并未在new Integer(1)時退出,說明未使用自定義的Integer,于是就保證了安全性。
JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。
1.由于Java的跨平臺性,經過編譯的Java源程序并不是一個可執行程序,而是一個或多個類文件。
2.當Java程序需要使用某個類時,JVM會確保這個類已經被加載、連接(驗證、準備和解析)和初始化。類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class文件,然后產生與所加載類對應的Class對象。加載完成后,Class對象還不完整,所以此時的類還不可用。
3.當類被加載后就進入連接階段,這一階段包括驗證、準備(為靜態變量分配內存并設置默認的初始值)和解析(將符號引用替換為直接引用)三個步驟。最后JVM對類進行初始化,包括:如果類存在直接的父類并且這個類還沒有被初始化,那么就先初始化父類;如果類中存在初始化語句,就依次執行這些初始化語句。
4.類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。
5.從Java 2(JDK 1.2)開始,類加載過程采取了父親委托機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載。JVM不會向Java程序提供對Bootstrap的引用。
● 下面是關于幾個類加載器的說明:
1.Bootstrap:一般用本地代碼實現,負責加載JVM基礎核心類庫(rt.jar);
2.Extension:從java.ext.dirs系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;
3.System:又叫應用類加載器,其父類是Extension。它是應用最廣泛的類加載器。它從環境變量classpath或者系統屬性java.class.path所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。
● 類型.class,例如:String.class;
● 對象.getClass(),例如:”hello”.getClass();
● Class.forName(),例如:Class.forName(“java.lang.String”);