zoukankan      html  css  js  c++  java
  • 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;
    
        }
    
      
    
    
    }
    View Code

    打印结果

    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: emp 下,删除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

     这里可以回顾下命名空间的概念

    每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。

    在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类。

    在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。

    不同类加载器的命名空间关系

    同一个命名空间内的类是相互可见的

    子加载器的命名空间包含所有父加载器的命名空间。因此由子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。

    由父加载器加载的类不能看见子加载器加载的类。

    如果两个类加载器之间没有直接或间接的父子关系,那么他们各自加载的类相互不可见。

    所以上面抛出异常的原因为:如果两个类加载器之间没有直接或间接的父子关系,那么他们各自加载的类相互不可见

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

    1、可以确保Java核心库的类型安全: 所有的Java应用都至少会引用java.lang.Object类,也就是说在运行期,java.lang.Object这个类

    会被加载到Java虚拟机中; 如果这个加载过程是由Java应用自己的类加载器所完成的,那么很可能就会 在JVM中存在多个版本的
    java.lang.Object类,而且这些类之间还是不兼容的,相互不可见的。(正是命名空间在发挥作用 )
    借助于双亲委托机制,Java核心类库中的类的加载工作都是由启动类加载器来统一完成。从而确保了Java应用所使用的都是同一个版本的
    Java核心类库,他们之间是相互兼容的。
    2、可以确保Java核心类库所提供的类不会被自定义的类所替代。
    3、不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间。相同名称的类可以并处在Java虚拟机中,只需要不同的类加载器来加载
    他们即可。不同类加载器所加载的类之间是不兼容的,这就相当于在Java虚拟机内部创建一个又一个相互隔离的Java类空间。这类技术在很多框架中
    都得到了应用。

  • 相关阅读:
    Ubuntu 19.04安装phpipam软件
    ubuntu snmp 安装与配置
    xcode 拷贝新的ios image 进去以后 出现 the divices is locked
    常用 Git 命令清单
    ios 从工程中删除Cocoapods
    ios app上架流程
    MySql某一列累计查询
    Docx4j将html转成word时,br标签为软回车的问题修改
    java面试题
    java获取classpath
  • 原文地址:https://www.cnblogs.com/linlf03/p/11028633.html
Copyright © 2011-2022 走看看