類的加載器

### 類的加載器

1. java類的加載器種類有哪些

1. 根類加載器---->C++編寫的,無法查看源碼
2. 擴展類加載器---->加載位置:jre\lib\ext中
3. 系統(應用)類加載器----->加載位置:classpath中
4. 自定義類加載器----->繼承ClassLoader

2. 類什麼時候被初始化?

1. 創建類的實例,也就是new一個對象
2. 訪問某個類或者接口的靜態變量,或者對該靜態變量進行賦值
3. 調用類的靜態方法
4. 反射(Class.forName("完全限定名"))
5. 初始化一個子類(首先會初始化子類的父類)
6. JVM啓動時表明的啓動類,即文件名與類名相同的那個類

3. 類的初始化步驟

1. 如果這個類還沒有被加載或者鏈接,那先進行加載和鏈接
2. 假如這個類存在直接父類,並且這個類還沒有被初始化(注意:在一個類加載器中,類只能初始化一次),那就直接初始化父類(不適用於接口)
3. 假如類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句

4. java類加載體系之ClassLoader雙親委託機制

1. java是一種類型安全的語言,它有四類稱爲沙箱安全機制的安全機制來保證語言的安全性,這四類安全沙箱分別是:

1. 類加載體系
2. .class文件檢驗器
3. 內置java虛擬機的安全特性
4. 安全管理及java API

2. java程序中的.java文件編譯完成會生成.class文件,而.class文件就是就是通過被稱爲類加載器的ClassLoader加載的,而ClassLoader在加載過程中會使用'雙親委派機制'來加載class文件

1. BootStrapClassLoader:啓動類加載器,該ClassLoader是JVM在啓動時創建的,用於加載$JAVA_HOME$/jre/lib下面的類庫。由於啓動類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啓動類加載器的引用,所以不能通過引用直接操作

2. ExtClassLoader:擴展類加載器,該ClassLoader是在sun.misc.launcher裏作爲一個內部類ExtClassLoader定義的(即sun.misc.Launcher$ExtClassLoader),ExtClassLoader會加載$JAVA_HOME$/jre/lib/ext下的類庫

3. AppClassLoader:應用程序類加載器,該加載器同樣在sun.misc.Launcher裏面作爲一個內部類AppClassLoader定義(即sun.misc.Launcher$AppClassLoader),AppClassLoader會加載java環境變量ClassPath所指定的路徑下的類庫,而ClassPath所指定的路徑可以通過System.getProperty("java.class.path")獲取;該變量也可以覆蓋,可以使用參數-cp,例如:java -cp路徑

4. CustomClassLoader:自定義類加載器,該ClassLoader是指我們自定義的ClassLoader,比如tomcat的StandardClassLoader屬於這一類;當然大部分情況下使用AppClassLoader就足夠了

5. 前面談到了ClassLoader的幾類加載器,而ClassLoader使用雙親委派機制來加載class文件。ClassLoader的雙親委派機制是這樣的(這裏先忽略掉自定義類加載器)

1. 當AppClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成

2. 當ExtClassLoader加載一個class時,它首先也不會自己去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成

3. 如果BootSrapeLoader加載失敗(例如在$JAVA_HOME$/jre/lib裏未查到該class),會使用ExtClassLoader來嘗試加載

4. 若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,如果AppClassLoader也加載失敗,則會報出異常ClassNotFoundException

5. 下面是ClassLoader的loadClass(String name,boolean resolve)的源碼:

```java
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,fasle);
}else{
//沒有父類。通過一下方法來加載
c=findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
//ClassNotFoundException thrown if class not found
//form the non-null parent class loader
}
if(c==null){
//如果還是沒有找到,調用findClass(name)去找這個類
c=findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
```

首先找緩存(findLoaderClass),沒有的話就判斷有沒有parent,如果有就使用parent來遞歸的loadClass,然而ExtClassLoader並沒有配置parent,則會通過findBootstrapClassOrNull來加載class,而findBootstrapClassOrNull則會使用JNI方法“private native Class findBootstrapClass(String name)”來使用BootStrapClassLoader來加載class;如果parent沒有找到class,則會調用findClass來加載class,findClass是一個protected的空方法,可以覆蓋t它以便自定義class加載過程。另外,雖然ClassLoader加載類是使用classLoader方法,但是鼓勵用ClassLoader的子類重寫findClass(String),而不是重寫classLoader,這樣就不會覆蓋了類加載默認的雙親委派機制。

5. 雙親委派爲什麼安全?

1. 例如:ClassLoader加載的class文件來源很多,比如編譯器生成的class或者網絡下載的字節碼。而一些來源的class文件時不可靠的,比如可以自定義一個java.lang.Integer類覆蓋JDK中默認的Integer類

```java
package java.lang;
public class Integer{
public Integer(int value){}
System.exit(0);
}
```

初始化這個類的構造器是會退出JVM,破壞應用程序的正常進行,如果使用雙親委派機制的話該Integer永遠不會被調用,以爲委託BootStrapClassLoader加載後會加載JDK中Integer類而不會加載自定義的這個,可以看看下面這個測試用例:

```java
poblic static void main(String[]args){
Integer i=new Integer(1);
System.err.println(i);

}
```

執行時並未在new Integer(1)時退出,說明未使用自定義的Integer,於是就保證了安全性

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章