Java ClassLoader 原理详细分析
作者:网络转载 发布时间:[ 2015/3/24 11:11:46 ] 推荐标签:Java ClassLoader JDK
3、但是JVM在搜索类的时候,又是如何判定两个class是相同的呢?
JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的。算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。比如网络上的一个Java类org.classloader.simple.NetClassLoaderSimple,javac编译之后生成字节码文件NetClassLoaderSimple.class,ClassLoaderA和ClassLoaderB这两个类加载器并读取了NetClassLoaderSimple.class文件,并分别定义出了java.lang.Class实例来表示这个类,对于JVM来说,它们是两个不同的实例对象,但它们确实是同一份字节码文件,如果试图将这个Class实例生成具体的对象进行转换时,会抛运行时异常java.lang.ClassCaseException,提示这是两个不同的类型。现在通过实例来验证上述所描述的是否正确:
1)、在web服务器上建一个org.classloader.simple.NetClassLoaderSimple.java类
package org.classloader.simple;
public class NetClassLoaderSimple{
private NetClassLoaderSimple instance;
public void setNetClassLoaderSimple(Object obj){
this.instance=(NetClassLoaderSimple)obj;
}
}
org.classloader.simple.NetClassLoaderSimple类的setNetClassLoaderSimple方法接收一个Object类型参数,并将它强制转换成org.classloader.simple.NetClassLoaderSimple类型。
2)、测试两个class是否相同(NetWorkClassLoader.java)
package classloader;
public class NewworkClassLoaderTest{
public static void main(String[]args){
try{
//测试加载网络中的class文件
String rootUrl="http://localhost:8080/httpweb/classes";
String className="org.classloader.simple.NetClassLoaderSimple";
NetworkClassLoader ncl1=new NetworkClassLoader(rootUrl);
NetworkClassLoader ncl2=new NetworkClassLoader(rootUrl);
Class<?>clazz1=ncl1.loadClass(className);
Class<?>clazz2=ncl2.loadClass(className);
Object obj1=clazz1.newInstance();
Object obj2=clazz2.newInstance();
clazz1.getMethod("setNetClassLoaderSimple",Object.class).invoke(obj1,obj2);
}catch(Exception e){
e.printStackTrace();
}
}
}
首先获得网络上一个class文件的二进制名称,然后通过自定义的类加载器NetworkClassLoader创建两个实例,并根据网络地址分别加载这份class,并得到这两个ClassLoader实例加载后生成的Class实例clazz1和clazz2,后将这两个Class实例分别生成具体的实例对象obj1和obj2,再通过反射调用clazz1中的setNetClassLoaderSimple方法。
3)、查看测试结果

结论:从结果中可以看出,虽然是同一份class字节码文件,但是由于被两个不同的ClassLoader实例所加载,所以JVM认为它们是两个不同的类。
4、ClassLoader的体系架构:

验证ClassLoader加载类的原理:
测试1:打印ClassLoader类的层次结构,请看下面这段代码:
ClassLoader loader=ClassLoaderTest.class.getClassLoader();//获得加载ClassLoaderTest.class这个类的类加载器
while(loader!=null){
System.out.println(loader);
loader=loader.getParent();//获得父类加载器的引用
}
System.out.println(loader);
打印结果:

第一行结果说明:ClassLoaderTest的类加载器是AppClassLoader。
第二行结果说明:AppClassLoader的类加器是ExtClassLoader,即parent=ExtClassLoader。
第三行结果说明:ExtClassLoader的类加器是Bootstrap ClassLoader,因为Bootstrap ClassLoader不是一个普通的Java类,所以ExtClassLoader的parent=null,所以第三行的打印结果为null是这个原因。

sales@spasvo.com