不同类加载器加载同一个class文件

不同类加载器加载同一个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)。

posted @ 2020-05-06 22:46  SanjiApollo  阅读(3119)  评论(0编辑  收藏  举报