08_JVM学习笔记_类命名空间解析

类命名空间

示例代码:

public class MyTest21 {
    public static void main(String[] args) throws Exception {
        MyTest16 loader1 = new MyTest16("loader1");
        MyTest16 loader2 = new MyTest16("loader2");

        loader1.setPath("/Users/leofight/Desktop/");
        loader2.setPath("/Users/leofight/Desktop/");

        Class clazz1 = loader1.loadClass("com.leofight.jvm.classloader.MyPerson");
        Class clazz2 = loader2.loadClass("com.leofight.jvm.classloader.MyPerson");

        System.out.println(clazz1 == clazz2);

        Object object1 = clazz1.newInstance();
        Object object2 = clazz2.newInstance();

        Method method = clazz1.getMethod("setMyPerson", Object.class);
        method.invoke(object1, object2);
    }
}

复制全部class文件到桌面,删除classpath下的MyPerson的class文件,然后运行程序,结果是?

输出

findClass invoked: com.leofight.jvm.classloader.MyPerson
class loader name: loader1
findClass invoked: com.leofight.jvm.classloader.MyPerson
class loader name: loader2
false
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.leofight.jvm.classloader.MyTest21.main(MyTest21.java:34)
Caused by: java.lang.ClassCastException: com.leofight.jvm.classloader.MyPerson cannot be cast to com.leofight.jvm.classloader.MyPerson
    at com.leofight.jvm.classloader.MyPerson.setMyPerson(MyPerson.java:8)
    ... 5 more

为什么结果是false呢?
loader1为MyPerson的定义类加载器
loader2为MyPerson的定义类加载器
loader1与loader2从双亲委托机制来看,它们是没有任何关联的,形成了两个命名空间。
如果两个加载器之间没有直接或者间接的父子关系,那么它们各自加载的类相互不可见。
所以两个不同的对象clazz1 == clazz2肯定为false。

为什么会抛异常?
两个对象是不可见的,其中一个命名空间的对象调用了另一个命名空间的对象,所以会抛异常。

小结

类加载器的双亲委托模型的好处

①可以确保Java核心库的类型安全:所有的Java应用都至少会引用java.lang.Object类,也就是说在运行期,java.lang.Object这个类会被加载到Java虚拟机中;如果这个加载过程是由Java应用自己的类加载器所完成的,那么很可能就会在JVM中存在多个版本的java.lang.Object,而且这些类之间还是不兼容的,相互不可见的(正是命名空间在发挥着作用)。借助于双亲委托机制,Java核心类库中的类的加载工作都是由启动类加载器来统一完成,从而确保了Java应用所使用的都是同一个版本的Java核心类库,他们之间是相互兼容的。

②可以确保Java核心类库所提供的类不会被自定义的类所替代。

③不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间。相同名称的类可以存在Java虚拟机中,只需要用不同的类加载器来加载他们即可。不同类加载器所加载的类之间是不兼容的,这相当于在Java虚拟机内部创建了一个又一个相互隔离的Java类空间,这类技术在很多框架中都得到了实际的应用。

示例代码

package com.leofight.jvm.classloader;

public class MyTest22 {

    static {
        System.out.println("MyTest22 initializer");
    }

    public static void main(String[] args) {

        System.out.println(MyTest22.class.getClassLoader());

        System.out.println(MyTest1.class.getClassLoader());
    }
}


输出

MyTest22 initializer
sun.misc.Launcher$AppClassLoader@14dad5dc
sun.misc.Launcher$AppClassLoader@14dad5dc

运行
➜ classes java -Djava.ext.dirs=./ com.leofight.jvm.classloader.MyTest22
输出

MyTest22 initializer
sun.misc.Launcher$AppClassLoader@4e0e2f2a
sun.misc.Launcher$AppClassLoader@4e0e2f2a

扩展类加载器是加载的jar包
执行jar cvf test.java com/leofight/jvm/classloader/MyTest1.class

jar cvf test.java com/leofight/jvm/classloader/MyTest1.class 
已添加清单
正在添加: com/leofight/jvm/classloader/MyTest1.class(输入 = 646) (输出 = 371)(压缩了 42%)

再次运行java -Djava.ext.dirs=./ com.leofight.jvm.classloader.MyTest22
输出

MyTest22 initializer
sun.misc.Launcher$AppClassLoader@659e0bfd
sun.misc.Launcher$ExtClassLoader@55f96302

在运行期,一个Java类是由该类的完全限定名(binary name,二进制名)和用于加载该类的定义类加载器(defining loader)所共同决定的。 如果同样名字(即相同的完全限定名)的类是由两个不同的加载器所加载,那么这些类就是不同的,即便.class文件的字节码完全一样,并且从相同的位置加载亦如此。

你可能感兴趣的:(08_JVM学习笔记_类命名空间解析)