1. 类加载器
类加载器:实现这个动作(通过一个类的全限定名来获取描述此类的二进制字节流)的代码模块被称为"类加载器"
类加载器不仅仅只用于实现类的加载动作,它还与类的相等判定有关。
判断类相等,有三个条件:
- 两个类来自同一个Class文件
- 两个类是由同一个虚拟机加载
- 两个类都是同一个类加载器加载
public class ClassLoaderTest {
public static void main(String[] args) throws Exception{
ClassLoader myLoder = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".")) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null){
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name,b,0,b.length);
}catch (IOException e){
throw new ClassNotFoundException(name);
}
}
};
// 通过重写loadClass方法,实现自己的加载器。结果发现出现了两个ClassLoaderTest类,
// 原因就是一个是系统类加载器加载的,另外一个则是由我们实现的类加载器加载。
Object obj = myLoder.loadClass("classloader.ClassLoaderTest").newInstance();
System.out.println(obj.getClass()); // class classloader.ClassLoaderTest
System.out.println(obj instanceof classloader.ClassLoaderTest); // false
}
}
JVM在运行时产生三个ClassLoader:
-
Bootstrap ClassLoader
: 启动类加载器,也叫根加载器。负责加载<JAVA_HOME>\lib
目录中的类库,也就是Java核心类。由C++实现,无法被Java程序直接引用。 -
Extension ClassLoader
扩展类加载器,负责加载<JAVA_HOME>\lib\ext
目录中的类库,用户可以将自己开发的类打包成jar包放在这个目录,即可扩展核心类以外的新功能。 - System ClassLoader \ App ClassLoader: 系统类加载器或称为应用程序加载器,一般情况下是程序中默认的类加载器。负责加载用户类路径(ClassPath)上所指定的类库。
2. 双亲委派机制
当一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器无法完成这个加载请求时,子类加载器才会尝试去自己加载。
类加载器之间的父子关系是采用组合关系来复用父加载器的代码。
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出 ClassNotFoundException
// 则说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载的时候
// 再调用本身的findClass方法来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
使用双亲委派机制,保证了无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此保证了类的唯一性。
3. 全盘负责
全盘负责是指 当一个ClassLoader装载一个类时,除非显示的使用另一个ClassLoader,则该类所依赖及引用的类也由这个ClassLoader载入。
网友评论