理解:父类加载器加载的类不能使用子类加载器加载的类

首先说一下类加载器加载类的先后关系即双亲委派模型:
在这里插入图片描述
例子在这里:

https://m.imooc.com/wenda/detail/495158

如果如果有一个类A是由扩展类加载器加载的,Class B是由系统类加载器加载的。在A中有一个函数,函数的参数是一个Object类型的。那么现在A调用这个函数时将这个B的实例传进去会报错。为什么呢?在这里A的加载器不会下去向子类加载器去询问,这个是关键,即使子类加载器加载了这个类,所以问题就是出现在了这里。
在这里插入图片描述
通过此图可以一目了然的看到答案,在当前的加载器的基础上想上查找,看看弗雷加载器有没有加载过,如果加载过那么就返回,如果没有那么就再向上查找,直到找到最顶级的类加载器。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过底层每一个类加载器的命名空间来保证类加载的安全性。越是子类加载器越具有包容性,越是上层加载器越局限。从第一张图中可以看出自定义类加载器的命名空间是最大的,启动类加载器的命名空间是最小的。
其实从其类加载的范围也是可以解决这个问题的,启动类加载器jre/lib下的文件,扩展类加载器加载的是jre/lib/ext下的文件,系统类加载器加载的是classpath下的文件,自定义类加载器加载的是自定义文件夹下的文件,所以从这一角度也是可以知道每一个类加载器从本身加载的范围来说是不尽相同的,再加上类继承关系所获得属性就可以知道父类加载器所加载的类,反之则不行,从路径来看如果自定义文件夹在F盘,classpath所在的路径在E盘,那么系统类加载器如何也不可能知道自定义加载器加载的类在什么地方。

https://blog.csdn.net/qq_20846769/article/details/100026116

这篇文章写的不错,综合对比了加载器之间的混合调用,并得出结论:子类加载器可以获取父类加载器加载的类,反之则不行。

并且同一个自定义类加载器的不同实例加载同一个类会在各自的类加载器的命名空间内部。

//自定义类加载器
public class EdClassLoader extends ClassLoader{
    String path;
    byte[] returncontext = new byte[1024*1024];
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File file = new File(getPath());
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //每次读取的大小是4kb
            byte[] b = new byte[4 * 1024];
            int n = 0;
            while ((n = fileInputStream.read(b)) != -1) {
//                下个将数据读入到字节数组中,再将字节数组输出到字节输出流中
                outputStream.write(b, 0, n);
            }
            //将Dog.class类读取到byte数组里
            returncontext = outputStream.toByteArray();
            //调用defineClass 将byte 加载成class
            Class<?> aClass = this.defineClass(name, returncontext, 0, returncontext.length);
            return aClass;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    public String getPath() {
        return path;
    }

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

    public byte[] getReturncontext() {
        return returncontext;
    }

    public void setReturncontext(byte[] returncontext) {
        this.returncontext = returncontext;
    }
}

在这里插入图片描述

//对比
public class testAppClassLoader {
    Person person = null;
    public testAppClassLoader(Object o,Object o1){
        System.out.println(o instanceof Person);
        System.out.println(o1 instanceof Person);
    }
}
 public static void main(String[] args) {
        try {
            EdClassLoader edClassLoader = new EdClassLoader();
            edClassLoader.setPath("C:\\Users\\mcl\\Desktop\\test\\sss\\Person.class");
            Class<?> person = edClassLoader.findClass("Person");
            Object o =  person.newInstance();
            EdClassLoader edClassLoader1 = new EdClassLoader();
            edClassLoader1.setPath("C:\\Users\\mcl\\Desktop\\test\\sss\\Person.class");
            Class<?> person1 = edClassLoader1.findClass("Person");
            Object o1 =  person1.newInstance();
            testAppClassLoader testAppClassLoader = new testAppClassLoader(o, o1);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
结果表明没有问题,且不可以向系统类加载器加载的转换。
再试一下,换成自定义来加载器是一样的:

public static void main(String[] args) {
//        new cas_test();
        try {
            EdClassLoader edClassLoader = new EdClassLoader();
            edClassLoader.setPath("C:\\Users\\mcl\\Desktop\\test\\sss\\Person.class");
            Class<?> person = edClassLoader.findClass("Person");
            Object o =  person.newInstance();
            edClassLoader.setPath("C:\\Users\\mcl\\Desktop\\test\\Person.class");
            Class<?> person1 = edClassLoader.findClass("Person");
            Object o1 =  person1.newInstance();
            testAppClassLoader testAppClassLoader = new testAppClassLoader(o, o1);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
提示类已经被加载过,无需再次加载,可以知道命名空间里已经有了Person,那么就不会再次加载。
这个问题是我面试阿里时面试官问的一个题目,一直百思不得其解。哎对于类加载器这里的学习还是不到位,直到最近仔细的学习了一下类加载。但是网上关于这个解释的并不是很到位或是长篇大论很难定位到具体的问题,于是为了加深理解,写了这篇博客。
如有错误请不吝赐教,感谢!

注:孤陋寡闻了,这篇文章写的很好之前没有看到,在此标注一下:

https://blog.csdn.net/codehole_/article/details/100892463

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值