摘要
Java的類裝載器是Java動態性的核心本文將向大家簡要介紹Java的類裝載器及相關的parent delegation模型命名空間運行時包等概念同時討論一些在學習中容易混淆的問題
類裝載器的功能及分類
顧名思義類裝載器是用來把類(class)裝載進JVM的JVM規范定義了兩種類型的類裝載器啟動類裝載器(bootstrap)和用戶自定義裝載器(userdefined class loader)
bootstrap是JVM自帶的類裝載器用來裝載核心類庫如javalang*等如javalangObject是由bootstrap裝載的
Java提供了抽象類ClassLoader
所有用戶自定義類裝載器都實例化自ClassLoader的子類
System Class Loader是一個特殊的用戶自定義類裝載器
由JVM的實現者提供
在編程者不特別指定裝載器的情況下默認裝載用戶類
系統類裝載器可以通過ClassLoader
getSystemClassLoader() 方法得到
例測試你所使用的JVM的ClassLoader
/*LoaderSamplejava*/
public class LoaderSample {
public static void main(String[] args) {
Class c;
ClassLoader cl;
cl = ClassLoadergetSystemClassLoader();
Systemoutprintln(cl);
while (cl != null) {
cl = clgetParent();
Systemoutprintln(cl);
}
try {
c = ClassforName(javalangObject);
cl = cgetClassLoader();
Systemoutprintln(javalangObjects loader is + cl);
c = ClassforName(LoaderSample);
cl = cgetClassLoader();
Systemoutprintln(LoaderSamples loader is + cl);
} catch (Exception e) {
eprintStackTrace();
}
}
}
在我的機器上(Sun Java )的運行結果
C:\java>java LoaderSample
sunmiscLauncher$AppClassLoader@ba
sunmiscLauncher$ExtClassLoader@e
null
javalangObjects loader is null
LoaderSamples loader is sunmiscLauncher$AppClassLoader@ba
第一行表示系統類裝載器實例化自類sunmiscLauncher$AppClassLoader
第二行表示系統類裝載器的parent實例化自類sunmiscLauncher$ExtClassLoader
第三行表示系統類裝載器parent的parent為bootstrap
第四行表示核心類javalangObject是由bootstrap裝載的
第五行表示用戶類LoaderSample是由系統類裝載器裝載的
parent delegation模型
從版本開始Java引入了雙親委托模型從而更好的保證Java平台的安全在此模型下當一個裝載器被請求裝載某個類時它首先委托自己的parent去裝載若parent能裝載則返回這個類所對應的Class對象若parent不能裝載則由parent的請求者去裝載
如圖所示loader的parent為loaderloader的parent為system class loader假設loader被要求裝載類MyClass在parent delegation模型下loader首先請求loader代為裝載loader再請求系統類裝載器去裝載MyClass若系統裝載器能成功裝載則將MyClass所對應的Class對象的reference返回給loaderloader再將reference返回給loader從而成功將類MyClass裝載進虛擬機若系統類裝載器不能裝載MyClassloader會嘗試裝載MyClass若loader也不能成功裝載loader會嘗試裝載若所有的parent及loader本身都不能裝載則裝載失敗
若有一個能成功裝載實際裝載的類裝載器被稱為定義類裝載器所有能成功返回Class對象的裝載器(包括定義類裝載器)被稱為初始類裝載器如圖所示假設loader實際裝載了MyClass則loader為MyClass的定義類裝載器loader和loader為MyClass的初始類裝載器
圖 parent delegation模型
需要指出的是Class Loader是對象它的父子關系和類的父子關系沒有任何關系一對父子loader可能實例化自同一個Class也可能不是甚至父loader實例化自子類子loader實例化自父類假設MyClassLoader繼承自ParentClassLoader我們可以有如下父子loader
ClassLoader loader = new MyClassLoader();
//參數 loader 為 parent
ClassLoader loader = new ParentClassLoader(loader);
那麼parent delegation模型為什麼更安全了?因為在此模型下用戶自定義的類裝載器不可能裝載應該由父親裝載器裝載的可靠類從而防止不可靠甚至惡意的代碼代替由父親裝載器裝載的可靠代碼實際上類裝載器的編寫者可以自由選擇不用把請求委托給parent但正如上所說會帶來安全的問題
命名空間及其作用
每個類裝載器有自己的命名空間命名空間由所有以此裝載器為初始類裝載器的類組成不同命名空間的兩個類是不可見的但只要得到類所對應的Class對象的reference還是可以訪問另一命名空間的類
例演示了一個命名空間的類如何使用另一命名空間的類在例子中LoaderSample由系統類裝載器裝載LoaderSample由自定義的裝載器loader負責裝載兩個類不在同一命名空間但LoaderSample得到了LoaderSample所對應的Class對象的reference所以它可以訪問LoaderSampl中公共的成員(如age)
例 不同命名空間的類的訪問
/*LoaderSamplejava*/
import *;
import javalangreflect*;
public class LoaderSample {
public static void main(String[] args) {
try {
String path = SystemgetProperty(userdir);
URL[] us = {new URL(file:// + path + /sub/)};
ClassLoader loader = new URLClassLoader(us);
Class c = loaderloadClass(LoaderSample);
Object o = cnewInstance();
Field f = cgetField(age);
int age = fgetInt(o);
Systemoutprintln(age is + age);
} catch (Exception e) {
eprintStackTrace();
}
}
}
/*sub/Loadersamplejava*/
public class LoaderSample {
static {
Systemoutprintln(LoaderSample loaded);
}
public int age = ;
}
編譯
javac LoaderSamplejava;
javac sub/LoaderSamplejava
運行java LoaderSample
LoaderSample loaded
age is
從運行結果中可以看出在類LoaderSample中可以創建處於另一命名空間的類LoaderSample中的對象並可以訪問其公共成員age
運行時包(runtime package)
由同一類裝載器定義裝載的屬於相同包的類組成了運行時包決定兩個類是不是屬於同一個運行時包不僅要看它們的包名是否相同還要看類裝載器是否相同只有屬於同一運行時包的類才能互相訪問包可見的類和成員這樣的限制避免了用戶自己的代碼冒充核心類庫的類訪問核心類庫包可見成員的情況假設用戶自己定義了一個類javalangYes並用用戶自定義的類裝載器裝載由於javalangYes和核心類庫javalang*由不同的裝載器裝載它們屬於不同的運行時包所以javalangYes不能訪問核心類庫javalang中類的包可見的成員
總結
在簡單討論了類裝載器parent delegation模型命名空間運行時包後相信大家已經對它們的作用有了一定的了解命名空間並沒有完全禁止屬於不同空間的類的互相訪問雙親委托模型加強了Java的安全運行時包增加了對包可見成員的保護
From:http://tw.wingwit.com/Article/program/Java/hx/201311/27180.html