JVM学习笔记17——类加载器双亲委托机制实力剖析

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

在MyTest16中加入输出,发现并不会执行findClass方法

System.out.println(o.getClass().getClassLoader());

输出类加载器看到是AppClassLoader加载的o

上述两点说明在Object o 并不是由我们自定义的类加载器加载的,这是因为双亲委托模型,我们自定义的类加载器委托给了系统类加载器

系统类加载器就是这个类的定义类加载器,系统类加载器和MyTest16就是他的初始类加载器

public class MyTest16 extends ClassLoader{

    private String classLoaderName;

    private String path;

    private final String fileExtension = ".class";

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

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

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

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

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

    private byte[] loadClassDate(String name){
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        name = name.replace(".","/");

        try {

            is = new FileInputStream(new File(this.path + name + this.fileExtension));
            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 (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    public static void main(String[] args) throws Exception{
        MyTest16 loader1 = new MyTest16("loader1");
        // 在本地建立指定目录,然后删掉idea编译的MyTest1,可以发现findClass执行了
        loader1.setPath("D:\\workspace\\");
        // loader1.setPath("/d/workspace/jvm_lecture/target/classes/");
        Class<?> clazz = loader1.loadClass("com.yshuoo.jvm.classloader.MyTest1");
        System.out.println("class: " + clazz.hashCode());
        Object object = clazz.newInstance();
        System.out.println(object);

    }
}

对MyTest16做出修改,可以指定路径去加载,在系统类加载器默认路径外创建一个完整的路径,之后编译好的MyTest1,执行发现findClass方法执行,证明我们自定义的类加载器从指定路径加载了MyTest1(删掉了原本target里的MyTest1.class 父类加载器——系统类加载器加载失败)

new 一个新的类加载器loader2,和loader1一样从同一的路径加载,重新构造项目恢复MyTest1

public static void main(String[] args) throws Exception{
        MyTest16 loader1 = new MyTest16("loader1");
        // 在本地建立指定目录,然后删掉idea编译的MyTest1,可以发现findClass执行了
        loader1.setPath("D:\\workspace\\");
        // loader1.setPath("/d/workspace/jvm_lecture/target/classes/");
        Class<?> clazz = loader1.loadClass("com.yshuoo.jvm.classloader.MyTest1");
        System.out.println("class: " + clazz.hashCode());
        Object object = clazz.newInstance();
        System.out.println(object);

        System.out.println("************");
        
        MyTest16 loader2 = new MyTest16("loader2");
        loader2.setPath("D:\\workspace\\");
        Class<?> clazz2 = loader2.loadClass("com.yshuoo.jvm.classloader.MyTest1");
        System.out.println("class: " + clazz2.hashCode());
        Object object2 = clazz2.newInstance();
        System.out.println(object2);
    }

可以看出系统类加载器第一次加载过了MyTest1,第二次不再重新加载

再把MyTest1.class删掉

执行结果如下:

两个类加载器都执行了,而且同一个类被加载了两次,这和命名空间有关系

  • 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
  • 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
  • 在不同的命名空间中,有可能会出现类的完整名字爱(包括类的包名)相同的两个类

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值