• JVM之类加载器下篇


    除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载。

                                    

    --启动类加载器,又称根加载器,是一个native的方法,使用c++实现。在java中我们用null标识,用于加载jdk自带的类。

    --扩展类加载器,用于加载jdk扩展类

    --系统类加载器,用于加载classpath目录下的类

                    


    上面提到的三种类加载器,是存在父子关系,即系统类加载器会委托extension加载器,如果extension加载器不能加载该类的话,再由系统类加载器进行加载。注意这里所说的父子关系不是指继承关系,而是一种组合关系。至于为什么要使用这种父委托加载机制呢?

    一个显而易见的好处就是Java类伴随着它的类加载器一起具备了一种带有优先级带层次关系,列如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载的,因此Object类在程序的各种类加载器环境中是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个成为java.lang.Object的类,并放在程序的classpath下面,那系统将出现多个Object类,java类型体系中最基础的行为也就无法保证类,应用程序也将会变得一片混乱。


    实现一个自定义类加载器只需继承ClassLoader,重写findClass()方法

    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    
    /**
     * @desc 
     * @author chenqm 
     * @date 2016年2月17日
     */
    public class MyClassLoader extends ClassLoader {
    
        private String name; //类加载器的名字
        private String path = "d:\"; //加载类的路径
        private String fileType = ".class";//Class文件的扩展名
        public MyClassLoader(String name){
            super();
            this.name = name;
        }
        
        public MyClassLoader(ClassLoader parent,String name){
            super(parent);//显式制定该类加载器的父加载器
        }
    
        public String getPath() {
            return path;
        }
    
        public void setPath(String path) {
            this.path = path;
        }
        
        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException{
           byte[] data = this.loadClassData(name);
           return this.defineClass(name, data, 0, data.length);
        }
        
        private byte[] loadClassData(String name){
            InputStream is = null;
            
            byte[] data = null;
            
            ByteArrayOutputStream baos = null;
            
            try{
                name = name.replace(".", "\");
                is = new FileInputStream(path + name +fileType);
                baos = new ByteArrayOutputStream();
                int ch =0 ; 
                while(-1 !=(ch = is.read())){
                    baos.write(ch);
                }
                data = baos.toByteArray();
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                try{
                    is.close();
                    baos.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
           
            return data;
        }
        
        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return name;
        }
    
        public static void main(String[] args) throws  Exception{
            MyClassLoader loader1 = new MyClassLoader("loader1");
            loader1.setPath("d:\myapp\serverlib\");
            MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
            loader2.setPath("d:\myapp\clientlib\");
            MyClassLoader loader3 = new MyClassLoader(null,"loader3");
            loader3.setPath("d:\myapp\otherlib\");
            test(loader1);
            test(loader2);
            test(loader3);
        }
        
        public static void test(ClassLoader loader) throws Exception{
            Class clazz = loader.loadClass("Simple");
            Object o = clazz.newInstance();
        }
    }

    这里我自定了一个类加载器,用于加载simple类

    /**
     * @desc 
     * @author chenqm 
     * @date 2016年2月17日
     */
    public class Simple {
        public Simple(){
            System.out.println("Sample is loaded by:"+this.getClass().getClassLoader());
            Dog dog = new Dog();
            System.out.println("Dog is loaded by:"+dog.getClass().getClassLoader());
        }
    }

    给出dog类:

    /**
     * @desc 
     * @author chenqm 
     * @date 2016年2月17日
     */
    public class Dog {
    
    }

    然后把Dog.class与Simple.class文件放到d:myappserverlib 和d:myappclientlib(两个路径下均有两个class),将MyClassLoader文件放到d:myappotherlib下面。

    进入d:myappotherlib 执行java MyClassLoader

    得到结果:

    Sample is loaded by:loader1
    Dog is loaded by:loader1
    Sample is loaded by:loader1
    Dog is loaded by:loader1
    java.io.FileNotFoundException: d:myappotherlibSimple.class (No such file or directory)

    简单分析一下,请注意MyClassLoader的main方法,有三个自定类加载器loader1(父加载器是系统类加载器),loader2(父加载器是loader1),loader3(父加载器是根加载器)。

    test方法:用指定的类加载器去加载simple类。

    --用loader1去加载simple类的时候,loader1的父加载器是系统类加载器,系统类加载器肯定无法加载simple类,为什么呢?前面说了,系统类加载器用于加载classpath下的类,而classpath默认是什么?  "."就是当前路径,而当前路径不存在simple类,所以进而由loader1加载。

    --loader2的父加载器是loader1,所以根据父委托加载机制,理应由loader1去加载simple类。

    --loader3的父加载器是根加载器,根加载器是不会加载我们自定义的类文件的,所以加载的重任就交给了loader3,但是loader3的目录下,不存在simple.class文件,没办法了,父亲干不了,自己也干不了,那只能抛异常咯。

    
    
    
  • 相关阅读:
    微信js sdk动态引用
    mysql
    github 常用
    使用Win32DiskImager后重置SD卡
    nuxt generate静态化后回退问题
    nuxt.config有关router配置
    vue本人常用插件汇总(常更新)
    Windows Server 2008 IIS 并发请求设置
    Python 高级编程 ——观察者模式
    MYSQL语句大全
  • 原文地址:https://www.cnblogs.com/think-in-java/p/5196254.html
Copyright © 2020-2023  润新知