JVM 类加载器命名空间深度解析与实例分析
Posted linlf03
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM 类加载器命名空间深度解析与实例分析相关的知识,希望对你有一定的参考价值。
一、创建Sample
1、创建实例
public class MyPerson private MyPerson myPerson; public void setMyPerson(Object obj) this.myPerson = (MyPerson)obj;
2、创建测试类
public class MyTest20 public static void main(String[] args) throws Exception MyTest16 loader1 = new MyTest16("loader1"); MyTest16 loader2 = new MyTest16("loader2"); Class<?> clazz1 = loader1.loadClass("com.example.jvm.classloader.MyPerson"); Class<?> clazz2 = loader2.loadClass("com.example.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);
3、MyTest16类和之前的一致
public class MyTest16 extends ClassLoader private String className; //目录 private String path; private final String fileExtension = ".class"; public MyTest16(String classLoadName) super(); //将系统类加载器当做该类加载器的父加载器 this.className = classLoadName; public MyTest16(ClassLoader parent, String classLoadName) super(parent); //显示指定该类加载器的父加载器器 this.className = classLoadName; public void setPath(String path) this.path = path; @Override public String toString() return "[" + this.className + "]"; @Override protected Class<?> findClass(String clasName) throws ClassNotFoundException System.out.println("findClass invoked:" + clasName); System.out.println("class loader name: " + this.className); byte[] data = this.loadClassData(clasName); return this.defineClass(clasName,data, 0, data.length); private byte[] loadClassData(String className) InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = null; try className = className.replace(".","//"); //System.out.println("className:" +this.className); is = new FileInputStream(new File(this.path + className + this.fileExtension)); baos = new ByteArrayOutputStream(); int ch = 0; while ( -1 != (ch = is.read())) baos.write(ch); data = baos.toByteArray(); catch (Exception ex) ex.printStackTrace(); finally try is.close(); baos.close(); catch (Exception ex) ex.printStackTrace(); return data;
打印结果
true
二、修改Sample
在一的基础上修改代码,设置path
loader1.setPath("D:/temp/");
loader2.setPath("D:/temp/");
public class MyTest21 public static void main(String[] args) throws Exception MyTest16 loader1 = new MyTest16("loader1"); MyTest16 loader2 = new MyTest16("loader2"); loader1.setPath("D:/temp/"); loader2.setPath("D:/temp/"); Class<?> clazz1 = loader1.loadClass("com.example.jvm.classloader.MyPerson"); Class<?> clazz2 = loader2.loadClass("com.example.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文件所在的build下的com文件夹拷贝到D:\\temp 下,删除build下的MyPerson.class 文件
打印结果:
findClass invoked:com.example.jvm.classloader.MyPerson class loader name: loader1 findClass invoked:com.example.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.example.jvm.classloader.MyTest21.main(MyTest21.java:27) Caused by: java.lang.ClassCastException: com.example.jvm.classloader.MyPerson cannot be cast to com.example.jvm.classloader.MyPerson at com.example.jvm.classloader.MyPerson.setMyPerson(MyPerson.java:11) ... 5 more
结果分析,loader1和loader2分别加载了MyPerson,分别给MyPerson分配了内存空间,如下图:
loader1和loader2是两个不同的命名空间。
所以System.out.println( clazz1 == clazz2);的结果为false
这里可以回顾下命名空间的概念
每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。
在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类。
在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。
不同类加载器的命名空间关系
同一个命名空间内的类是相互可见的
子加载器的命名空间包含所有父加载器的命名空间。因此由子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。
由父加载器加载的类不能看见子加载器加载的类。
如果两个类加载器之间没有直接或间接的父子关系,那么他们各自加载的类相互不可见。
所以上面抛出异常的原因为:如果两个类加载器之间没有直接或间接的父子关系,那么他们各自加载的类相互不可见
以上是关于JVM 类加载器命名空间深度解析与实例分析的主要内容,如果未能解决你的问题,请参考以下文章