• JVM 自定义类加载器


    一、创建自定义类加载器

    package com.example.jvm.classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    /**
     * Created by Think on 2019/6/9.
     */
    public class MyTest16  extends  ClassLoader{
    
        private String classLoadName;
    
        private final String fileExtension = ".class";
    
        public MyTest16(String classLoadName){
            super(); //将系统类加载器当做该类加载器的父加载器
            this.classLoadName = classLoadName;
        }
    
        public MyTest16(ClassLoader parent, String classLoadName){
            super(parent); //显示指定该类加载器的父加载器器
            this.classLoadName = classLoadName;
        }
    
        @Override
        public String toString() {
            return "[" + this.classLoadName + "]";
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] data = this.loadClassData(classLoadName);
            return  this.defineClass(classLoadName,data, 0, data.length);
        }
    
        private byte[] loadClassData(String name){
            InputStream is = null;
            byte[] data = null;
            ByteArrayOutputStream baos = null;
    
            try{
                this.classLoadName = this.classLoadName.replace(".","//");
                is = new FileInputStream(new File(name + 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;
    
        }
    
        public static void main(String[] args)  throws Exception{
            MyTest16 loader1 = new MyTest16("loader1");
            test(loader1);
        }
    
    
        public static  void test(ClassLoader classLoader) throws Exception {
            Class<?> clazz = classLoader.loadClass("com.example.jvm.classloader.MyTest1");
            Object object = clazz.newInstance();
            System.out.println(object);
    
        }
    }
    

      打印结果

    com.example.jvm.classloader.MyTest1@1540e19d
    

      

    二、完善上一个实例创建的类加载器

    命名空间:

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

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

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

    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("className11:" +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;
    
        }
    
        public static void main(String[] args)  throws Exception{
            MyTest16 loader1 = new MyTest16("loader1");
            //loader1.setPath("D:/workspace/study/ jvm_demo/build/classes/java/main/");
            loader1.setPath("D:/temp/"); // 将 D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader  MyTest1文件移动到 D:/temp/com/example/jvm/classloader目录下
            Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class:" + clazz.hashCode());
            Object object = clazz.newInstance();
            System.out.println(object);
            //System.out.println(object.getClass().getClassLoader());
    
    
            MyTest16 loader2 = new MyTest16("loader2");
            loader2.setPath("D:/temp/");
            Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class2:" + clazz2.hashCode());
            Object object2 = clazz2.newInstance();
            System.out.println(object2);
     
        }
    
    
    
    }
    

      将 D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader  MyTest1文件移动到 D:/temp/com/example/jvm/classloader目录下

    打印结果:

    findClass invoked:com.example.jvm.classloader.MyTest1
    class loader name: loader1
    className11:loader1
    class:21685669
    com.example.jvm.classloader.MyTest1@7f31245a
    
    findClass invoked:com.example.jvm.classloader.MyTest1
    class loader name: loader2
    className11:loader2
    class2:1173230247
    com.example.jvm.classloader.MyTest1@330bedb4
    

      1)loader1 和loader2 是两个实例,构成了两个不同的命名空间。

    此时使用的是自定义类加载器。两个类的hascode值是不一样的。

    2)如果将D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader  MyTest1文件不移除,

    打印的结果:

    class:356573597
    com.example.jvm.classloader.MyTest1@677327b6
    
    class2:356573597
    com.example.jvm.classloader.MyTest1@14ae5a5
    

      两个列的hasCode值是一样的,是同一个值。使用的类加载器是APP类加载器。 因为测试,父加载器会加载D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader的MyTest1.class 文件,加载到了。自定义类加载器就不需要在加载了。

    三、在二的基础上进行改造

    package com.example.jvm.classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    /**
     * Created by Think on 2019/6/9.
     */
    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;
    
        }
    
        public static void main(String[] args)  throws Exception{
            MyTest16 loader1 = new MyTest16("loader1");
            //loader1.setPath("D:/workspace/study/ jvm_demo/build/classes/java/main/");
            loader1.setPath("D:/temp/"); //删除 将 D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader  MyTest1文件移动到 D:/temp/com/example/jvm/classloader目录下
            Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class:" + clazz.hashCode());
            Object object = clazz.newInstance();
            System.out.println(object);
            System.out.println();
    
            MyTest16 loader2 = new MyTest16(loader1,"loader2");
            loader2.setPath("D:/temp/");
            Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class:" + clazz2.hashCode());
            Object object2 = clazz2.newInstance();
            System.out.println(object2);
    
        }
    
    
    
    }
    

      删除工程里的MyTest1.class,保留D: emp 目录下的MyTest1.class 文件。

    设置MyTest16 loader2 = new MyTest16(loader1,"loader2");

    输出结果如下:

    findClass invoked:com.example.jvm.classloader.MyTest1
    class loader name: loader1
    class:21685669
    com.example.jvm.classloader.MyTest1@7f31245a
    
    class:21685669
    com.example.jvm.classloader.MyTest1@6d6f6e28
    

      hashCode值都是21685669

     MyTest1由自定义loader1加载,

    loader2委托loader1加载,loader1已经加载过了MyTest类,所以loader2不需要加载了。

    四、在三的基础上进行改造

     public static void main(String[] args)  throws Exception{
            MyTest16 loader1 = new MyTest16("loader1");
            //loader1.setPath("D:/workspace/study/ jvm_demo/build/classes/java/main/");
            loader1.setPath("D:/temp/"); 
            Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class:" + clazz.hashCode());
            Object object = clazz.newInstance();
            System.out.println(object);
            System.out.println();
    
            MyTest16 loader2 = new MyTest16(loader1,"loader2");
            loader2.setPath("D:/temp/");
            Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class:" + clazz2.hashCode());
            Object object2 = clazz2.newInstance();
            System.out.println(object2);
            System.out.println();
    
            MyTest16 loader3 = new MyTest16(loader2,"loader3");
            loader3.setPath("D:/temp/");
            Class<?> clazz3 = loader3.loadClass("com.example.jvm.classloader.MyTest1"); //
            System.out.println("class:" + clazz3.hashCode());
            Object object3 = clazz3.newInstance();
            System.out.println(object3);
            System.out.println();
    
    
    
        }
    

     删除工程下的的MyTest1.class,保留D: emp 下的MyTest1.class文件, 打印结果

    findClass invoked:com.example.jvm.classloader.MyTest1
    class loader name: loader1
    class:21685669
    com.example.jvm.classloader.MyTest1@7f31245a
    
    class:21685669
    com.example.jvm.classloader.MyTest1@6d6f6e28
    
    class:21685669
    com.example.jvm.classloader.MyTest1@135fbaa4
    

      

  • 相关阅读:
    gc的real时间比user时间长
    java 反射: 当Timestamp类型的属性值为null时,设置默认值
    多线程注意点
    多线程socket编程示例
    一个类有两个方法,其中一个是同步的,另一个是非同步的; 现在又两个线程A和B,请问:当线程A访问此类的同步方法时,线程B是否能访问此类的非同步方法?
    含有Date和Timestamp的Java和Json互相转化
    java bean、List、数组、map和Json的相互转化
    并发抢购
    SQL 性能调优日常积累【转】
    String和包装类IntegerDoubleLongFloatCharacter 都是final类型
  • 原文地址:https://www.cnblogs.com/linlf03/p/10995208.html
Copyright © 2020-2023  润新知