Java源码阅读:类加载器问题实例
在Java中,类加载器(ClassLoader)是负责将类(.class文件)加载到JVM中的组件。Java提供了几种不同的类加载器,包括:
- 启动类加载器(Bootstrap ClassLoader):用C++实现,是虚拟机自带的类加载器,负责加载JAVA_HOME/jre/lib目录中的核心类库,或者-Xbootclasspath参数指定的路径中的类。
- 扩展类加载器(Extension ClassLoader):由Java语言实现,继承自ClassLoader类,负责加载JAVA_HOME/jre/lib/ext目录或者由系统属性java.ext.dirs指定位置中的类库。
- 应用程序类加载器(Application ClassLoader):也称为系统类加载器,由Java语言实现,继承自ClassLoader类,负责加载环境变量classpath或系统属性java.class.path指定路径下的类库。
类加载器之间存在一个层次关系,称为类加载器的双亲委派模型(Parent Delegation Model)。在这个模型中,如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,如果父类加载器还存在其父类加载器,则进一步向上委派,请求一直传递到顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回;如果父类加载器无法完成此加载任务,子加载器才尝试自己去加载。
下面是一个简单的类加载器问题实例:
假设我们有两个类,A
和B
,B
继承自A
。A
类在系统类路径中,而B
类在用户自定义的类路径中。
```java// A.javapublic class A {
public void show() {
System.out.println(“A’s show()”);
}
}
// B.javapublic class B extends A {
public void show() {
System.out.println(“B’s show()”);
}
}``如果我们尝试加载
B类,并且调用
show()方法,根据双亲委派模型,JVM会首先尝试使用系统类加载器加载
B类,但由于
B类不在系统类路径中,系统类加载器会委托给扩展类加载器,扩展类加载器也无法加载,最后委托给启动类加载器,启动类加载器也无法加载,因为
B类不在核心类库中。此时,系统类加载器会尝试从用户自定义的类路径中加载
B类,成功加载后,调用
show()`方法,输出”B’s show()”。
但如果我们尝试直接加载A
类,并尝试调用B
类的show()
方法,由于A
类和B
类不在同一个类加载器中,这将导致ClassCastException
,因为A
类加载器加载的A
对象实际上无法转换为B
类。
```javapublic class Main {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@Override public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals(“B”)) {
byte[] b = …; //从文件系统或其他来源加载B类的字节码 return defineClass(name, b,0, b.length);
}
return super.loadClass(name);
}
};
Class<?> aClass = Class.forName(“A”);
Class<?> bClass = myLoader.loadClass(“B”);
A a = (A) bClass.newInstance();
a.show(); //这里会抛出ClassCastException }
}``在这个例子中,
A类和
B类被不同的类加载器加载,因此即使
B是
A的子类,也不能将
B的实例强制转换为
A`的类型,因为它们在JVM中是两个完全不同的类。这就是类加载器问题的一个实例。
还没有评论,来说两句吧...