虛擬機器載入機制之類載入器
阿新 • • 發佈:2018-11-11
文章目錄
在類的載入過程中的載入階段,我們使用到了類載入器。
一、在虛擬機器中,類的唯一性是怎樣確保的。
在虛擬機器中,是通過載入類的類載入器和類來唯一確定一個類的。所以就算連個不同的類載入器載入的class檔案一模一樣,這兩個類在虛擬機器中也不是同一個類。那麼用instanceof、equeals()方法、isAssignableFrom()、isInstance()方法也得不到想要的結果。
例:
package fengli;
import java.io.IOException;
import java.io.InputStream;
public class Main {
public static void main(String[] args) throws Exception {
ClassLoader cl = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
try {
byte [] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException(name);
}
}
};
Object obj1=cl.loadClass("fengli.Main").newInstance();
Object obj2=new Main();
System.out.println(obj1.getClass());//class fengli.Main
System.out.println(obj2.getClass());//class fengli.Main
System.out.println(obj1 instanceof fengli.Main);//false
System.out.println(obj2 instanceof fengli.Main);//true
}
}
執行結果:
二、雙親委派模型
從虛擬機器角度,只存在兩種不同的類載入器。
- 一種是啟動類載入器(BootStrap ClassLoader),這個類載入器使用C++實現,是虛擬機器的一部分。
- 另一個就是其他類載入器。都繼承與java.lang.ClassLoader。這是有java語言實現的,獨立於虛擬機器外部。
然而從開發人員角度,有三種類載入器。至於這三類載入器,我在:類的載入機制具體說道它們的區別了。
- 啟動類載入器
- 擴充套件類載入器
- 應用程式類載入器,也稱為系統類載入器。
雙親委派的類載入器之間的層次圖。他們不是以繼承關係組織的,而是用組合關係來複用父類載入器。
雙親委派的載入過程是這樣的:
當用一個類載入器區載入Class檔案時,先讓其父類載入器區載入這個Class檔案,當其父類不能載入這個Class檔案時(在搜尋範圍沒有找到這個類),才讓這個類區載入Class檔案。
器原始碼如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded(首先,檢查這個類是否已經被載入)
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//如果沒有,就嘗試使用父類載入器區載入這個類(name)
if (parent != null) {
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
}
//如果父類載入器在搜尋範圍內沒有對應的類,就呼叫自己的findClass方法進行載入。
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
使用雙親委派的好處是類隨著它的類載入器一同具備了層次關係。因此Object類在雙親委派下,始終是同一個類。
三、破壞雙親委派模型
雙親委派並不是一種強制性的約束模型,而是java設計者推薦給開發者的一種類載入器實現方式。在歷史上,雙親委派模型發生過三次大規模“被破壞”(並不具有貶義,指的是一種創新)情況。
第一次:添加了一個新的protected的finaClass().
第二次:是由於雙親委派的缺陷造成的,父類載入器不能呼叫子類載入器。於是使用執行緒上下文載入器區解決這個問題。
第三次:是由於使用者對程式的動態性(程式碼熱替換、模組熱部署)的追求造成的。