不同类加载器加载同一个class文件得到的类型也是不同的。
验证如下:
在D:\00-test
目录下,有名为Test.class
的文件,其编译前的源码如下:
public class Test {
public static int count = 0;
public Test() {
++count;
System.out.println(this.getClass().getClassLoader());
System.out.println("count: " + count);
}
}
每当创建一个对象的时候,静态变量count
就会自增,可以利用count
的值来判定不同类加载器得到的Class对象是否是同一个。
创建Main
类如下:
public class Main {
public static void main(String[] args) throws Exception {
URLClassLoader loader_1 =
new URLClassLoader(new URL[]{new File("D:\00-test").toURI().toURL()});
URLClassLoader loader_2 =
new URLClassLoader(new URL[]{new File("D:\00-test").toURI().toURL()});
Class<?> clazz_1 = loader_1.loadClass("Test");
clazz_1.newInstance();
Class<?> clazz_2 = loader_2.loadClass("Test");
clazz_2.newInstance();
}
}
在main()
方法中,首先自定义两个类加载器,分别使用这两个类加载器加载Test.class
文件,然后使用得到的Class对象创建实例,最后得到结果如下:
java.net.URLClassLoader@1b6d3586
count: 1
java.net.URLClassLoader@74a14482
count: 1
可以看到,两个对象的类加载器并不相同,count
值却是相同的,这说明这两个实例虽然都是从Test.class
中得来,类型却并不相同,从而count
只自增一次。
实际上,在jvms8的5.3.2节有这样一段阐述:
At run time, a class or interface is determined not by its name alone, but by a pair:
its binary name (§4.2.1) and its defining class loader. Each such class or interface
belongs to a single run-time package. The run-time package of a class or interface is
determined by the package name and defining class loader of the class or interface.
翻译如下:
在运行时,一个类或者接口并不是单单由它的名称确定的,而是由它的二进制名称以及它的定义类加载器共同确定的。
每个这样的类或者接口都属于一个运行时包,运行时包则由包名和定义类加载器共同确定。
上述中所谓的定义类加载器实际上就是getClassLoader()
的返回值,而这个概念的产生则跟类加载过程中的“双亲委派机制”有关。
当要加载一个类时,第一个发起类加载的加载器称之为“初始类加载器”(initiating loader),但是根据“双亲委派机制”,它会先将加载委派给父加载器,如果父加载器加载失败,才会最终由自己尝试加载。而无论哪个加载器加载成功,它就是该类的定义类加载器(defining class loader)。