JAVA的类加载器,详细解释(一)

2014-11-24 09:17:11 · 作者: · 浏览: 2
JVM规范定义了两种类型的类装载器:启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader)。
一. ClassLoader基本概念
1.ClassLoader分类
类装载器是用来把类(class)装载进JVM的。
JVM规范定义了两种类型的类装载器:启动内装载器(bootstrap)和用户自定义装载器(user-defined class loader)。
JVM在运行时会产生三个ClassLoader:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.Bootstrap是用C++编写的,我们在Java中看不到它,是null,是JVM自带的类装载器,用来装载核心类库,如java.lang.*等。
AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。
Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。 System Class Loader是一个特殊的用户自定义类装载器,由JVM的实现者提供,在 编程者不特别指定装载器的情况下默认装载用户类。系统类装载器可以通过ClassLoader.getSystemClassLoader() 方法得到。
例1,测试你所使用的JVM的ClassLoader
/*LoaderSample1.java*/
public class LoaderSample1 {
public static void main(String[] args) {
Class c;
ClassLoader cl;
cl = ClassLoader.getSystemClassLoader();
System.out.println(cl);
while (cl != null ) {
cl = cl.getParent();
System.out.println(cl);
}
try {
c = Class.forName( " java.lang.Object " );
cl = c.getClassLoader();
System.out.println( " java.lang.Object's loader is " + cl);
c = Class.forName( " LoaderSample1 " );
cl = c.getClassLoader();
System.out.println( " LoaderSample1's loader is " + cl);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在我的机器上(Sun Java 1.4.2)的运行结果
sun.misc.Launcher$AppClassLoader@1a0c10f
sun.misc.Launcher$ExtClassLoader@e2eec8
null
java.lang.Object's loader is null
LoaderSample1's loader is sun.misc.Launcher$AppClassLoader@1a0c10f
第一行表示,
系统
类装载器实例化自类sun.misc.Launcher$AppClassLoader
第二行表示,系统类装载器的parent实例化自类sun.misc.Launcher$ExtClassLoader
第三行表示,系统类装载器parent的parent为bootstrap
第四行表示,核心类java.lang.Object是由bootstrap装载的
第五行表示,用户类LoaderSample1是由系统类装载器装载的
二.命名空间及其作用
每个类装载器有自己的命名空间,命名空间由所有以此装载器为创始类装载器的类组成。不同命名空间的两个类是不可见的,但只要得到类所对应的Class对象的reference,还是可以访问另一命名空间的类。
例2演示了一个命名空间的类如何使用另一命名空间的类。在例子中,LoaderSample2由系统类装载器装载,LoaderSample3由自定义的装载器loader负责装载,两个类不在同一命名空间,但LoaderSample2得到了LoaderSample3所对应的Class对象的reference,所以它可以访问LoaderSampl3中公共的成员(如age)。
例2不同命名空间的类的访问
/*LoaderSample2.java*/
import java.net. * ;
import java.lang.reflect. * ;
public class LoaderSample2 {
public static void main(String[] args) {
try {
String path = System.getProperty( " user.dir " );
URL[] us = { new URL( " file:// " + path + " /sub/ " )};
ClassLoader loader = new URLClassLoader(us);
Class c = loader.loadClass( " LoaderSample3 " );
Object o = c.newInstance();
Field f = c.getField( " age " );
int age = f.getInt(o);
System.out.println( " age is " + age);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*sub/Loadersample3.java*/
public class LoaderSample3 {
static {
System.out.println( " LoaderSample3 loaded " );
}
public int age = 30 ;
}
编译:javac LoaderSample2.java; javac sub/LoaderSample3.java
运行:java LoaderSample2
LoaderSample3 loaded
age is 30
从运行结果中可以看出,在类LoaderSample2中可以创建处于另一命名空间的类LoaderSample3中的对象并可以访问其公共成员age。
运行时包(runtime package)
由同一类装载器定义装载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看的定义类装载器是否相同。只有属于同一运行时包的类才能互相访问包可见的类和成员。这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见