命名空间深度解析
直接先上例子
package com.ssy.jvm.classloader;
import java.lang.reflect.Method;
public class MyTest21 {
public static void main(String[] args) throws Exception {
MyFirstClassLoader loader1 = new MyFirstClassLoader("loader1");
MyFirstClassLoader loader2 = new MyFirstClassLoader("loader2");
loader1.setPath("/Users/ddcc/Desktop");
loader2.setPath("/Users/ddcc/Desktop");
Class<?> clazz1 = loader1.loadClass("com.ssy.jvm.classloader.MyPerson");
Class<?> clazz2 = loader2.loadClass("com.ssy.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);
}
}
package com.ssy.jvm.classloader;
public class MyPerson {
private MyPerson myPerson;
public void setMyPerson(Object myPerson) {
this.myPerson = (MyPerson) myPerson;
}
}
我们先将classpath中的MyPerson.class文件保留
结果:
true
解析:
经过上一节类加载器的讲解,我们知道,类加载的过程是通过双亲委托机制来完成的。loader1和loader2都会交由它的父加载器–系统类加载器 去进行加载,而很明显,系统类加载器可以加载MyPerson类。当loader1交由系统类加载器加载后,loader2再交由系统类加载器加载时,发现这个类已经被加载了,那么会直接返回这个类的Class对象,所以结果为True。
我们现在将classpath中的MyPerson.class删除,在Desktop上放这个文件。
结果
findClass invoked: com.ssy.jvm.classloader.MyPerson
class loader name: loader1
findClass invoked: com.ssy.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:498)
at com.ssy.jvm.classloader.MyTest21.main(MyTest21.java:22)
Caused by: java.lang.ClassCastException: com.ssy.jvm.classloader.MyPerson cannot be cast to com.ssy.jvm.classloader.MyPerson
at com.ssy.jvm.classloader.MyPerson.setMyPerson(MyPerson.java:7)
... 5 more
分析
这个结果就非常好玩了。首先打印出false,其次,抛出了异常。我们分别来解释。
loader1和loader2是没有直接或间接父子关系的两个加载器。所以它们在各自的命名空间内。
同一个命名空间内的类是相互可见的
子加载器的命名空间包含父加载器的命名空间,因此子加载器加载的类能看到父加载器所加载的类
父加载器所加载的类看不到子加载器所加载的类。
如果两个加载器之间没有直接或者间接的父子关系,那么相互不可见
所以loader1加载的Class对象和loader2加载的Class对象是相互不可见的。由此,它们new出来的对象也是相互不可见的。当用反射调用方法将loader2加载的Class对象所对应的类的实例传入时,转换为loader1加载的Class对象所对应的类中,就会抛出类转换异常。
单独看java.lang.ClassCastException: com.ssy.jvm.classloader.MyPerson cannot be cast to com.ssy.jvm.classloader.MyPerson
这个异常的时候,真的是不知所云,哈哈哈。。