美文网首页
类加载范围

类加载范围

作者: shz_Minato | 来源:发表于2019-02-18 10:48 被阅读1次

    类加载范围

     系统类加载器加载的为项目中的class
     每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成
     如果被加载的类中引用着其他的类,比如

    public class Sample {
        public Sample() {
            System.out.println("Sample is loaded by: " + this.getClass().getClassLoader());
    
            new Cat();
        }
    
    }
    
    public class Cat {
        public Cat() {
            System.out.println("Cat is loaded by: " + this.getClass().getClassLoader());
        }
    }
    
    Sample类的构造方法中构造了Cat类(是对Cat的主动使用,会引起Cat的加载流程)。
    
    为了解释方便Sample类为 宿主类,Cat类为被引用类。
    

    加载Sample类(宿主类)的类加载器,就是Cat类(被引用类)的类加载流程的双亲委托的起点。
     比如Sample类的类加载器为系统类加载器,那么Cat类加载器为系统类加载器。如果Sample类的类加载器为自定义类加载器,那么Cat类加载流程的起点就是自定义类加载器。

    案例分析

     被加载的类

    public class Sample {
        public Sample() {
            System.out.println("Sample is loaded by: " + this.getClass().getClassLoader());
    
            new Cat();
        }
    
    }
    
    public class Cat {
        public Cat() {
            System.out.println("Cat is loaded by: " + this.getClass().getClassLoader());
        }
    }
    

     自定义的类加载器(从指定的路径加载class)

    public class MyClassLoader extends ClassLoader {
        private String classLoaderName;
        private String fileExtension = ".class";
        private String path;
    
        //以系统类加载器为父加载器
        public MyClassLoader(String classLoaderName) {
            super();
            this.classLoaderName = classLoaderName;
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    
        //指定父加载器
        public MyClassLoader(ClassLoader parent, String classLoaderName) {
            super(parent);
            this.classLoaderName = classLoaderName;
        }
    
        
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            System.out.println("findClass invoke: " + name);
            System.out.println("class loader name: " + classLoaderName);
            byte[] classData = getClassData(name);
            return defineClass(null, classData, 0, classData.length);
        }
    
        public byte[] getClassData(String className) {
            byte[] bytes = null;
            InputStream is = null;
            ByteArrayOutputStream bos = null;
    
            className = className.replaceAll("\\.", "\\\\");
            try {
    
                File file = new File(this.path, className + fileExtension);
    
                is = new FileInputStream(file);
                bos = new ByteArrayOutputStream();
    
                int ch = 0;
                while ((ch = is.read()) != -1) {
                    bos.write(ch);
                }
    
                bytes = bos.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                    bos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return bytes;
        }
    }
    

     分情况执行
      加载指定路径的Sample类,Sample类和Cat类都不在项目中

    public class Test {
        public static void main(String[] args) throws Exception {
           MyClassLoader classLoader = new MyClassLoader("loader1");
           
            classLoader.setPath("路径");
            //加载Sample类
            Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter19.Sample");
        
            System.out.println("-----");
            //构造Sample实例(执行构造方法)
            Object instance = loadClass.newInstance();
       }
    }
    
    //指定结果
    findClass invoke: com.minato.jvm.chapter19.Sample
    class loader name: loader1  //加载Sample时,执行的findClass方法
    -----
    Sample is loaded by: com.minato.jvm.chapter19.MyClassLoader@677327b6 //执行Sample的构造方法
    findClass invoke: com.minato.jvm.chapter19.Cat
    class loader name: loader1 //加载Cat时,执行的findClass方法
    Cat is loaded by: com.minato.jvm.chapter19.MyClassLoader@677327b6 //Cat的类加载器 
    
    //以上说明 Sample类和Cat类的加载器为同一个类加载器
    

      加载指定路径的Sample类,但Sample类的class文件仍存在项目中,Cat类不在项目中

    
    public class Test {
      
        public static void main(String[] args) throws Exception {
            //第一处
            MyClassLoader classLoader = new MyClassLoader("loader1");
            classLoader.setPath("C:\\Users\\shz\\Desktop\\");
            //第二处
            Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter19.Sample");
    
            System.out.println("-----");
            //第三处
            Object instance = loadClass.newInstance();
       }
    }
    
    //执行结果
    Sample is loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2 //执行Sample加载的类加载器
    Caused by: java.lang.ClassNotFoundException: com.minato.jvm.chapter19.Cat //报错
    
    //结果分析
    第一处:classLoader的的父加载器为系统类加载器就是AppClassLoader
    第二处:执行双亲委托的类加载流程,首先使用父加载器(系统类加载器)去加载,
    系统类加载器会检查此项目中是否存在class文件,因为Sample的class文件存在项目中,
    因此Sample的类加载器是系统类加载器。
    报错原因:
        因为Sample类的加载器为系统类加载器,因此系统类加载器加载Cat类,项目中不存在Cat的class文件,因此会报错。
    

      加载指定路径的Sample类,但Sample类的class文件不在项目中,Cat类在项目中

    public class Test {
       
        public static void main(String[] args) throws Exception {
            //classloader的父加载器为系统类加载器
            MyClassLoader classLoader = new MyClassLoader("loader1");
            classLoader.setPath("C:\\Users\\shz\\Desktop\\");
            Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter19.Sample");
    
            System.out.println("-----");
            Object instance = loadClass.newInstance();
       }
    }
    
    //执行结果
    findClass invoke: com.minato.jvm.chapter19.Sample
    class loader name: loader1 //加载Sample时,执行的findClass方法
    -----
    Sample is loaded by: com.minato.jvm.chapter19.MyClassLoader@677327b6 //Sample的类加载器-----自定义类加载
    Cat is loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2 //Cat的类加载器----·系统类加载器
    
    //结果分析
    classloader加载Sample类,按照双亲委托模型,层层委托,都不可加载,因此自定义类加载
    执行Sample的加载。 
    加载Cat时,使用宿主的类加载加载Cat,因为项目中存在Cat的类class文件,按照双亲委托模型,
    自定义类加载类加载器委托至系统类加载器,因为项目中存在Cat的class文件,因此使用系统类加载器去加载Cat文件。
    

    加载范围的性质

     结合命名空间的性质,空间中承载的是按照双亲委托模型可加载的类。
      子加载器所加载的类能够 访问 父加载器加载的类
      而父加载器所加载的类 不能访问 自加载器所加载的类

    相关文章

      网友评论

          本文标题:类加载范围

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