摘要
Java的类装载器是Java动态性的核心本文将向大家简要介绍Java的类装载器及相关的parentdelegation模型命名空间运行时包等概念同时讨论一些在学习中容易混淆的问题
类装载器的功能及分类
顾名思义类装载器是用来把类(class)装载进JVM的JVM规范定义了两种类型的类装载器启动类装载器(bootstrap)和用户自定义装载器(userdefinedclassloader)
bootstrap是JVM自带的类装载器用来装载核心类库如javalang*等如javalangObject是由bootstrap装载的
Java提供了抽象类ClassLoader
所有用户自定义类装载器都实例化自ClassLoader的子类
SystemClassLoader是一个特殊的用户自定义类装载器
由JVM的实现者提供
在编程者不特别指定装载器的情况下默认装载用户类
系统类装载器可以通过ClassLoader
getSystemClassLoader()方法得到
例测试你所使用的JVM的ClassLoader
/*LoaderSamplejava*/
publicclassLoaderSample{
publicstaticvoidmain(String[]args){
Classc;
ClassLoadercl;
cl=ClassLoadergetSystemClassLoader();
Systemoutprintln(cl);
while(cl!=null){
cl=clgetParent();
Systemoutprintln(cl);
}
try{
c=ClassforName(javalangObject);
cl=cgetClassLoader();
Systemoutprintln(javalangObjectsloaderis+cl);
c=ClassforName(LoaderSample);
cl=cgetClassLoader();
Systemoutprintln(LoaderSamplesloaderis+cl);
}catch(Exceptione){
eprintStackTrace();
}
}
}
在我的机器上(SunJava)的运行结果
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是由系统类装载器装载的
parentdelegation模型
从版本开始Java引入了双亲委托模型从而更好的保证Java平台的安全在此模型下当一个装载器被请求装载某个类时它首先委托自己的parent去装载若parent能装载则返回这个类所对应的Class对象若parent不能装载则由parent的请求者去装载
如图所示loader的parent为loaderloader的parent为systemclassloader假设loader被要求装载类MyClass在parentdelegation模型下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的初始类装载器
图parentdelegation模型
需要指出的是ClassLoader是对象它的父子关系和类的父子关系没有任何关系一对父子loader可能实例化自同一个Class也可能不是甚至父loader实例化自子类子loader实例化自父类假设MyClassLoader继承自ParentClassLoader我们可以有如下父子loader
ClassLoaderloader=newMyClassLoader();//参数loader为parentClassLoaderloader=newParentClassLoader(loader);
那么parentdelegation模型为什么更安全了?因为在此模型下用户自定义的类装载器不可能装载应该由父亲装载器装载的可靠类从而防止不可靠甚至恶意的代码代替由父亲装载器装载的可靠代码实际上类装载器的编写者可以自由选择不用把请求委托给parent但正如上所说会带来安全的问题
命名空间及其作用
每个类装载器有自己的命名空间命名空间由所有以此装载器为初始类装载器的类组成不同命名空间的两个类是不可见的但只要得到类所对应的Class对象的reference还是可以访问另一命名空间的类
例演示了一个命名空间的类如何使用另一命名空间的类在例子中LoaderSample由系统类装载器装载LoaderSample由自定义的装载器loader负责装载两个类不在同一命名空间但LoaderSample得到了LoaderSample所对应的Class对象的reference所以它可以访问LoaderSampl中公共的成员(如age)
例不同命名空间的类的访问
/*LoaderSamplejava*/
import*;
importjavalangreflect*;
publicclassLoaderSample{
publicstaticvoidmain(String[]args){
try{
Stringpath=SystemgetProperty(userdir);
URL[]us={newURL(file://+path+/sub/)};
ClassLoaderloader=newURLClassLoader(us);
Classc=loaderloadClass(LoaderSample);
Objecto=cnewInstance();
Fieldf=cgetField(age);
intage=fgetInt(o);
Systemoutprintln(ageis+age);
}catch(Exceptione){
eprintStackTrace();
}
}
}
/*sub/Loadersamplejava*/
publicclassLoaderSample{
static{
Systemoutprintln(LoaderSampleloaded);
}
publicintage=;
}
编译
javacLoaderSamplejava;
javacsub/LoaderSamplejava
运行javaLoaderSample
LoaderSampleloadedageis
从运行结果中可以看出在类LoaderSample中可以创建处于另一命名空间的类LoaderSample中的对象并可以访问其公共成员age
运行时包(runtimepackage)
由同一类装载器定义装载的属于相同包的类组成了运行时包决定两个类是不是属于同一个运行时包不仅要看它们的包名是否相同还要看类装载器是否相同只有属于同一运行时包的类才能互相访问包可见的类和成员这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况假设用户自己定义了一个类javalangYes并用用户自定义的类装载器装载由于javalangYes和核心类库javalang*由不同的装载器装载它们属于不同的运行时包所以javalangYes不能访问核心类库javalang中类的包可见的成员
总结
在简单讨论了类装载器parentdelegation模型命名空间运行时包后相信大家已经对它们的作用有了一定的了解命名空间并没有完全禁止属于不同空间的类的互相访问双亲委托模型加强了Java的安全运行时包增加了对包可见成员的保护