美文网首页
类加载3

类加载3

作者: 得力小泡泡 | 来源:发表于2020-11-22 21:29 被阅读0次

    MyTest类(自定义加载器)

    package com.test;
    
    import java.io.*;
    
    public class MyTest extends ClassLoader {
    
        private String classLoaderName;//加载器名字
    
        private String path;
    
        private final String fileExtension = ".class";
    
        public MyTest(String classLoaderName)
        {
            super();// 将系统类加载器当做该类加载器的父加载器
            this.classLoaderName = classLoaderName;
        }
        public MyTest(ClassLoader parent, String classLoaderName)
        {
            super(parent);// 显示指定该类加载器的父加载器
            this.classLoaderName = classLoaderName;
        }
    
        public void setPath(String path)
        {
            this.path = path;
        }
    
        @Override
        protected Class<?> findClass(String classname) throws ClassNotFoundException {
    
            System.out.println("findClass invoked:" + classname);
            System.out.println("class loader name: " + this.classLoaderName);
    
            byte[] data = this.loadClassData(classname);
    
            return this.defineClass(classname, data, 0, data.length);
        }
    
        private byte[] loadClassData(String className)
        {
            InputStream is = null;
            byte[] data = null;
            ByteArrayOutputStream baos = null;
    
            className = className.replace(".", "/");
            try {
    
                is = new FileInputStream(new File(this.path + className + this.fileExtension));
                baos = new ByteArrayOutputStream();
    
                int ch = 0;
                while(((ch = is.read()) != -1))
                {
                    baos.write(ch);
                }
    
                data = baos.toByteArray();
    
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                try {
                    is.close();
                    baos.close();
                } catch (Exception ex){
                    ex.printStackTrace();
                }
            }
    
            return data;
        }
    }
    

    MyCat类

    package com.test;
    
    public class MyCat {
    
        public MyCat() {
            System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
        }
    }
    

    MySample类

    package com.test;
    
    public class MySample {
    
        public MySample() {
            System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
    
            new MyCat();
        }
    }
    

    MyTest2类

    package com.test;
    
    public class MyTest2 {
        public static void main(String[] args) throws Exception {
            MyTest loader1 = new MyTest("loader1");
            loader1.setPath("C:/Users/小呆呆/Desktop/");//当父类加载器都解决不了的时候,才会调用自身的findClass往这个地址找
            
            Class<?> clazz = loader1.loadClass("com.test.MySample");
            System.out.println("class:" + clazz.hashCode());
    
            // 如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
            // 因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat.class
            Object object = clazz.newInstance();
        }
    }
    

    在项目中存在MyTest.class,MyTest2.class,桌面上的com.test中存在MyCat.class和MySample.class,当实例化MySample时,初始化时会对MyCat进行实例化

    • 1、当项目中存在MyCat.class和存在MySample.class时
      加载MySample.class是由系统类加载器进行加载,实例化MySample类时,需要对MyCat进行实例化,因此在MyCat实例化前,对MyCat进行加载,也是由系统类加载器进行加载

    输出

    class:1735600054
    MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    
    • 2、当项目中不存在MyCat.class和不存在MySample.class时
      系统类加载器加载不了MySample.class,因此由自定义加载器MyTest对MySample.class进行加载,实例化MySample类时,需要对MyCat进行实例化,因此在MyCat实例化前,由加载MySample的类加载器(MyTest自定义加载器)对MyCat.class进行加载
      疑问?
      是系统类加载器加载不了才会让MyTest去加载它,还是因为MySample是由MyTest加载的,所以要让MyTest去加载它?
      答案:后者是正确的,MySample.class是由MyTest加载的,MyCat是在MySample里面的,因此MyCat.class让加载MySample的类加载器(自定义加载器)直接进行加载的,
    findClass invoked:com.test.MySample
    class loader name: loader1
    class:2133927002
    MySample is loaded by:com.test.MyTest@677327b6
    findClass invoked:com.test.MyCat
    class loader name: loader1
    MyCat is loaded by:com.test.MyTest@677327b6
    
    • 3、当项目不存在MyCat.class,存在MySample.class时
      由于项目中存在MySample.class,因此系统类加载器可以对MySample.class进行加载,当对MySample类进行实例化时,需要对MyCat进行实例化,因为MyCat是在MySample里面的,在加载MyCat时是让加载MySample的类加载器(系统类加载器)直接进行加载的(因为父亲都加载不了)
      输出(存在找不到MyCat对象的错误)
    class:1735600054
    MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    Exception in thread "main" java.lang.NoClassDefFoundError: com/test/MyCat
        at com.test.MySample.<init>(MySample.java:8)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.lang.Class.newInstance(Class.java:442)
        at com.test.MyTest2.main(MyTest2.java:13)
    Caused by: java.lang.ClassNotFoundException: com.test.MyCat
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 7 more
    
    
    • 4、当项目存在MyCat.class,不存在MySample.class时
      由于项目中不存在MySample.class,需要自定义加载MyTest才能对它进行加载,当对MySample进行实例化时,需要对MyCat进行实例化,因为MyCat是在MySample里面的,在加载MyCat时是让加载MySample的类加载器(自定义加载器)直接进行加载的,又因为类加载器符合双亲委派机制,因此最终是由系统类加载器对MyCat进行加载

    输出

    findClass invoked:com.test.MySample
    class loader name: loader1
    class:2133927002
    MySample is loaded by:com.test.MyTest@677327b6
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    

    命名空间的关系

    image.png

    理论
    存在两个类

    • 如果二者在“同一个命名空间”下,则相互访问没有问题。

    • 如果一个在子命名空间,一个在父命名空间,则在子命名空间的类可以访问到父命名空间的类,而父命名空间中的类访问不到子命名空间的类(这种情况会报错java.lang.ClassNotFoundException)。

    • 如果二者在两个没有任何父子关系的同级命名空间中,则不能相互访问。


    MyTest2

    package com.test;
    
    public class MyTest2 {
        public static void main(String[] args) throws Exception {
            MyTest loader1 = new MyTest("loader1");
            loader1.setPath("C:/Users/小呆呆/Desktop/");//当父类加载器都解决不了的时候,才会调用自身的findClass往这个地址找
            
            Class<?> clazz = loader1.loadClass("com.test.MySample");
            System.out.println("class:" + clazz.hashCode());
    
            // 如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
            // 因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat.class
            Object object = clazz.newInstance();
        }
    }
    

    总结果:
    项目存在MyCat.class,不存在MySample.class时
    通过上面的分析可知,最终MySample类由MyTest自定义加载器进行加载,MyCat由系统类加载器进行加载,则MySample类可以访问MyCat,而MyCat不能访问MySample

    • 1、修改MyCat,加了一句代码引用MySample(其中MySample不引用Cat)

    MyCat类(和上面的MyCat不同)

    package com.test;
    
    public class MyCat {
    
        public MyCat() {
            System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
    
            System.out.println("from MyCat:" + MySample.class);
        }
    }
    

    MySample类

    package com.test;
    
    public class MySample {
    
        public MySample() {
            System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
    
            new MyCat();
        }
    }
    

    输出(报错)

    findClass invoked:com.test.MySample
    class loader name: loader1
    class:2133927002
    MySample is loaded by:com.test.MyTest@677327b6
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    Exception in thread "main" java.lang.NoClassDefFoundError: com/test/MySample
        at com.test.MyCat.<init>(MyCat.java:8)
        at com.test.MySample.<init>(MySample.java:8)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.lang.Class.newInstance(Class.java:442)
        at com.test.MyTest2.main(MyTest2.java:13)
    Caused by: java.lang.ClassNotFoundException: com.test.MySample
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 8 more
    
    • 2、修改了MySample,加了一句代码引用MyCat(其中MyCat不引用MySample)

    MyCat

    public class MyCat {
    
        public MyCat() {
            System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
    
            //System.out.println("from MyCat:" + MySample.class);
        }
    }
    
    

    MySample

    package com.test;
    
    public class MySample {
    
        public MySample() {
            System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
    
            new MyCat();
    
            System.out.println("from MySample:"  +  MyCat.class);
        }
    }
    
    

    输出

    findClass invoked:com.test.MySample
    class loader name: loader1
    class:2133927002
    MySample is loaded by:com.test.MyTest@677327b6
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    from MySample:class com.test.MyCat
    

    相关文章

      网友评论

          本文标题:类加载3

          本文链接:https://www.haomeiwen.com/subject/dirpiktx.html