类与类加载器
对于任何一个类,都需要有加载它的类加载器和这个类本身一通确立其在java虚拟机中的唯一性。每一个类加载器,都需要有一个独立的类名称空间。
更通俗一点的意思是:如果需要比较两个类是否相等
,只有当这两个类都是同一个类加载器加载的前提下,才能进行比较,否则如果加载这两个类的加载器完全不同,则这两个类必定是不一样的。
package com.deem.classload.test;
import java.io.InputStream;
public class ClassLoadTest {
public static void main(String[] args) throws Exception {
ClassLoader myClassLoad = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String className = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(className);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (Exception e) {
throw new ClassNotFoundException(name);
}
}
};
Object obj = myClassLoad.loadClass("com.deem.classload.test.ClassLoadTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof com.deem.classload.test.ClassLoadTest );
}
}
运行结果为
class com.deem.classload.test.ClassLoadTest
false
从第一个输出结果来看,这个对象确实是类com.deem.classload.test.ClassLoadTest实例化出来的对象
第二句 最比较时,返回 false,这个对象时通过我们自定义的类加载器进行加载的,其实此时虚拟机中存在了两个ClassLoadTest类,一个是系统应用程序自己加载的,另一个则是我们自己定义的类加载器进行加载的,虽然都是同一个Class文件,但是依然是两个独立的类,故而结果是false
类加载器与双亲委派机制
JVM预定义的三种类型类加载器:
- 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
- 标准扩展(Extension)类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
- 系统(System)类加载器:是由 Sun 的
AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。
除了以上列举的三种类加载器,还有一种比较特殊的类型 — 线程上下文类加载器。
双亲委派机制描述
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
参考
- 周志明,深入理解Java虚拟机:JVM高级特性与最佳实践,机械工业出版社
- Alexia(minmin)博客,http://www.cnblogs.com/lanxuezaipiao/p/4138511.html
- 笨鸟快飞的博客,http://blog.csdn.net/u011080472/article/details/51332866