JVM学习笔记4-类加载器代码示例及命名空间

重写findClass()方法

  • 类加载的双亲委托机制
public class MyTest16 extends ClassLoader{
    private String classLoaderName;
    private final String fileExtension = ".class";

    public MyTest16(String classLoaderName){
        //将系统类加载器当做该类加载器的父加载器
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTest16(ClassLoader parent, String classLoaderName){
        //显示指定该类加载器的父加载器
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    private byte[] loadClassData(String name){
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            this.classLoaderName = this.classLoaderName.replaceAll(".", "/");
            is = new FileInputStream(new File(name + fileExtension));
            int ch = 0;
            while ((ch = is.read()) != -1){
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return data;
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("我执行了");
        byte[] data = this.loadClassData(className);
        return this.defineClass(className, data, 0, data.length);
    }

    @Override
    public String toString() {
        return "MyTest16{" +
                "classLoaderName='" + classLoaderName + '\'' +
                ", fileExtension='" + fileExtension + '\'' +
                '}';
    }
    public static void main(String[] args) throws Exception {
        MyTest16 load1 = new MyTest16("load1");
        test(load1);
    }
    public static void  test(ClassLoader classLoader) throws Exception {
        Class<?> clazz = classLoader.loadClass("main.jvm.classloader.MyTest1");
        Object obj = clazz.newInstance();
        System.out.println(obj);
        System.out.println(obj.getClass().getClassLoader());
    }
}

执行结果:

main.jvm.classloader.MyTest1@5679c6c6
sun.misc.Launcher$AppClassLoader@18b4aac2

结论:

  • 由于双亲委托机制,导致MyTest1是由MyTest16的父加载器AppClassLoader来加载

使用自定义加载器加载类

public class MyTest16 extends ClassLoader {
    private String classLoaderName;

    private final String fileExtension = ".class";

    private String path;

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public MyTest16(String classLoaderName) {
        //将系统类加载器当做该类加载器的父加载器
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTest16(ClassLoader parent, String classLoaderName) {
        //显示指定该类加载器的父加载器
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        className = className.replaceAll("\\.", "/");

        try {
            String filePath = this.path + className + fileExtension;
            System.out.println("filePath="+filePath);
            is = new FileInputStream(new File(filePath));
            int ch = 0;
            while ((ch = is.read()) != -1) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        byte[] data = this.loadClassData(className);
        return this.defineClass(className, data, 0, data.length);
    }

    @Override
    public String toString() {
        return "MyTest16{" +
                "classLoaderName='" + classLoaderName + '\'' +
                ", fileExtension='" + fileExtension + '\'' +
                '}';
    }

    public static void main(String[] args) throws Exception {
        MyTest16 load1 = new MyTest16("load1");
//        load1.setPath("/Users/lutingfeng/Documents/idea/jvm_learn/out/production/jvm_learn/main/jvm/classloader");
        load1.setPath("/Users/lutingfeng/Desktop/");

        Class<?> clazz = load1.loadClass("main.jvm.classloader.MyTest1");
        System.out.println("clazz.hashCode="+clazz.hashCode());
        Object obj = clazz.newInstance();
        System.out.println(obj);
        System.out.println(obj.getClass().getClassLoader());
    }
}

执行结果:

clazz.hashCode=621009875
main.jvm.classloader.MyTest1@4b67cf4d
sun.misc.Launcher$AppClassLoader@135fbaa4

当删除classPath下的MyTest1.class文件后,执行结果:

filePath=/Users/lutingfeng/Desktop/main/jvm/classloader/MyTest1.class
clazz.hashCode=668849042
main.jvm.classloader.MyTest1@19e1023e
MyTest16{classLoaderName=‘load1’, fileExtension=’.class’}

命名空间

public class MyTest16 extends ClassLoader {
    private String classLoaderName;

    private final String fileExtension = ".class";

    private String path;

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public MyTest16(String classLoaderName) {
        //将系统类加载器当做该类加载器的父加载器
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTest16(ClassLoader parent, String classLoaderName) {
        //显示指定该类加载器的父加载器
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        className = className.replaceAll("\\.", "/");

        try {
            String filePath = this.path + className + fileExtension;
            System.out.println("filePath="+filePath);
            is = new FileInputStream(new File(filePath));
            int ch = 0;
            while ((ch = is.read()) != -1) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        byte[] data = this.loadClassData(className);
        return this.defineClass(className, data, 0, data.length);
    }

    @Override
    public String toString() {
        return "MyTest16{" +
                "classLoaderName='" + classLoaderName + '\'' +
                ", fileExtension='" + fileExtension + '\'' +
                '}';
    }

    public static void main(String[] args) throws Exception {
        MyTest16 load1 = new MyTest16("load1");
//        load1.setPath("/Users/lutingfeng/Documents/idea/jvm_learn/out/production/jvm_learn/main/jvm/classloader");
        load1.setPath("/Users/lutingfeng/Desktop/");

        Class<?> clazz1 = load1.loadClass("main.jvm.classloader.MyTest1");
        System.out.println("clazz.hashCode="+clazz1.hashCode());
        Object obj1 = clazz1.newInstance();
        System.out.println(obj1);
        System.out.println("classLoad="+obj1.getClass().getClassLoader());

        System.out.println("----");

        MyTest16 load2= new MyTest16("load2");
        load2.setPath("/Users/lutingfeng/Desktop/");
        Class<?> clazz2 = load2.loadClass("main.jvm.classloader.MyTest1");
        System.out.println("clazz.hashCode="+clazz2.hashCode());
        Object obj2 = clazz2.newInstance();
        System.out.println(obj2);
        System.out.println("classLoad="+obj2.getClass().getClassLoader());
    }
}

当删除classPath下的MyTest1.class文件后,执行结果:

filePath=/Users/lutingfeng/Desktop/main/jvm/classloader/MyTest1.class
clazz.hashCode=2125039532
main.jvm.classloader.MyTest1@12a3a380
classLoad=MyTest16{classLoaderName=‘load1’, fileExtension=’.class’}
++++
filePath=/Users/lutingfeng/Desktop/main/jvm/classloader/MyTest1.class
clazz.hashCode=1846274136
main.jvm.classloader.MyTest1@61bbe9ba
classLoad=MyTest16{classLoaderName=‘load2’, fileExtension=’.class’}

由执行结果可以看出.load1加载的MyTest1对应的hashCode与load2加载的hashCode不一致,其原因是:

  • 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
  • 在同一个命名空间中,不会出现类的完整名字(包括类的报名)相同的两个类
  • 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类
    补充两点:
  • 子加载器其所加载类能够访问到父加载器所加载的类
          因此由子加载器加载类能看见父加载器加载的类—系统类加载器加载的类能看见根加载器加载的类
  • 父加载器所加载的类不能访问到子加载器所加载的类
          如果两个加载器加密没有直接或间接的父子关系,那么它们各自加载的类相互不可见
public class MyTest16 extends ClassLoader {
    private String classLoaderName;

    private final String fileExtension = ".class";

    private String path;

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public MyTest16(String classLoaderName) {
        //将系统类加载器当做该类加载器的父加载器
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTest16(ClassLoader parent, String classLoaderName) {
        //显示指定该类加载器的父加载器
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        className = className.replaceAll("\\.", "/");

        try {
            String filePath = this.path + className + fileExtension;
            is = new FileInputStream(new File(filePath));
            int ch = 0;
            while ((ch = is.read()) != -1) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("findClass invoked : "+ className);
        byte[] data = this.loadClassData(className);
        return this.defineClass(className, data, 0, data.length);
    }

    @Override
    public String toString() {
        return "MyTest16{" +
                "classLoaderName='" + classLoaderName + '\'' +
                ", fileExtension='" + fileExtension + '\'' +
                '}';
    }

    public static void main(String[] args) throws Exception {
        MyTest16 load1 = new MyTest16("load1");
//        load1.setPath("/Users/lutingfeng/Documents/idea/jvm_learn/out/production/jvm_learn/main/jvm/classloader");
        load1.setPath("/Users/lutingfeng/Desktop/");

        Class<?> clazz1 = load1.loadClass("main.jvm.classloader.MyTest1");
        System.out.println("clazz.hashCode="+clazz1.hashCode());
        Object obj1 = clazz1.newInstance();
        System.out.println(obj1);
        System.out.println("classLoad="+obj1.getClass().getClassLoader());

        System.out.println("++++++++");

        MyTest16 load2= new MyTest16(load1, "load2");
        load2.setPath("/Users/lutingfeng/Desktop/");
        Class<?> clazz2 = load2.loadClass("main.jvm.classloader.MyTest1");
        System.out.println("clazz.hashCode="+clazz2.hashCode());
        Object obj2 = clazz2.newInstance();
        System.out.println(obj2);
        System.out.println("classLoad="+obj2.getClass().getClassLoader());

        System.out.println("++++++++");

        MyTest16 load3= new MyTest16(load2, "load3");
        load3.setPath("/Users/lutingfeng/Desktop/");
        Class<?> clazz3 = load3.loadClass("main.jvm.classloader.MyTest1");
        System.out.println("clazz.hashCode="+clazz3.hashCode());
        Object obj3 = clazz3.newInstance();
        System.out.println(obj3);
        System.out.println("classLoad="+obj3.getClass().getClassLoader());
    }
}

当删除classPath下的MyTest1.class文件后,执行结果:

findClass invoked : main.jvm.classloader.MyTest1
clazz.hashCode=2125039532
main.jvm.classloader.MyTest1@12a3a380
classLoad=MyTest16{classLoaderName=‘load1’, fileExtension=’.class’}
++++++++
clazz.hashCode=2125039532
main.jvm.classloader.MyTest1@29453f44
classLoad=MyTest16{classLoaderName=‘load1’, fileExtension=’.class’}
++++++++
clazz.hashCode=2125039532
main.jvm.classloader.MyTest1@5cad8086
classLoad=MyTest16{classLoaderName=‘load1’, fileExtension=’.class’}

你可能感兴趣的:(JVM学习笔记4-类加载器代码示例及命名空间)