类加载器的命名空间

本文的父子加载器不代表有继承关系,仅仅只是一种上下级的表达。

命名空间的概念

  • 每个类加载器都有各自的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
  • 在同一个命名空间中,不会出现全限定类名相同的两个Class对象
  • 在不同的命名空间中,可以出现全限定类名相同的两个Class对象
  • 父加载器加载的类对其子加载器可见,子加载器加载的类对其父加载器不可见
  • 如果两个加载器之间没有直接或间接父子的关系,那么它们各自加载的类相互不可见

案例验证

1.定义一个Student类,里面有一个Student类型的成员变量

public class Student{private Student student;//setter方法的参数是Object类型public void setStudent(Object object) {this.student = (Student) object;}
}

2.当Student.class字节码文件存在类路径下时

public class Demo {public static void main(String[] args) throws ClassNotFoundException,IllegalAccessException, InstantiationException,InvocationTargetException,NoSuchMethodException{//获取线程上下文类加载器,默认是Application ClassLoaderClassLoader classLoader = Thread.currentThread().getContextClassLoader();//使用类加载器加载两个Class对象Class c1 = classLoader.loadClass("Student");Class c2 = classLoader.loadClass("Student");System.out.println(c1 == c2);//使用两个Class对象分别创建一个新的实例(不使用强行转换)Object o1 = c1.newInstance();Object o2 = c2.newInstance();//获取setStudent方法Method m1 = c1.getMethod("setStudent",Object.class);//使用o1对象调用方法,o2对象作为参数传入m1.invoke(o1,o2);System.out.println(o1);}
}

运行结果如下

true
Student{student=Student{student=null}}
  • c1 == c2 返回的结果是true,说明在堆内存中同一个类的Class对象有且仅有一个。(学过反射的同学都一定听过这句话,但是这个结论的前提是,在同一个命名空间中。)
  • 赋值成功说明这两个实例是同一个类型。

为什么这里说堆内存中同一个类的Class对象有且仅有一个的前提是同一个命名空间中呢?请看下面的例子

这里自定义了一个类加载器,会把本地D盘下myclasspath文件夹下指定名称的字节码文件加载成类。

public class Demo {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {//创建两个自定义类加载器的对线下MyClassLoader c1 = new MyClassLoader();MyClassLoader c2 = new MyClassLoader();//分别使用两个类加载器加载Student类Class s1 = c1.loadClass("Student");Class s2 = c2.loadClass("Student");System.out.println(s1 == s2);Object o1 = s1.newInstance();Object o2 = s2.newInstance();Method m1 = s1.getMethod("setStudent", Object.class);m1.invoke(o1,o2);System.out.println(o1);}
}class MyClassLoader extends ClassLoader{@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String path = "D:\\myclasspth\\" + name + ".class";try {//创建byte数组输出流ByteArrayOutputStream os = new ByteArrayOutputStream();//把字节码文件复制到流中Files.copy(Paths.get(path),os);//得到字节数组byte[] bytes = os.toByteArray();//调用父类的defineClass方法加载类return defineClass(name,bytes,0,bytes.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException("找不到类文件");}}
}

如无意外,试了第一个例子的伙伴们会发现,这个程序输出结果和第一个例子一模一样。那么我提出这个例子的意义在哪里呢?

  • 根据类加载器的双亲委派机制,自定义类加载器 < 应用类加载器 < 拓展类加载器 < 启动类加载器,自定义类加载器加载Student这个类的时候首先会去父加载器中检查是否已经加载过,以此类推。
  • 因此,尝试过第一个例子的伙伴们在类路径下一定有Student.class这个字节码文件,因此这里实际使用的类加载器还是Application ClassLoader(有所怀疑的朋友可以通过class对象调用getClassLoader方法验证一下)

现在,我们把类路径下的Student.class文件复制到D盘的myclasspath文件夹下,原文件删除(使用IDEA的伙伴要把Student类也删除,不然运行的时候很可能就会自动生成字节码文件),然后重新运行程序。

这次的运行结果如下:

这个运行结果的重点在于

  1. s1 == s2 返回的结果是false
  2. 报错信息中的Student cannot be cast to Student

为什么s1 == s2返回的结果是false呢?

答案很简单,加载Student类的时候根据双亲委派机制发现类路径下也没有Student.class文件,因此就要自己去加载。而根据命名空间的概念,类加载器c1和c2没有任何父子关系,因此并不在同一个命名空间中,他们互相不知道对方加载过Student类,所以就会有两个Student类的Class对象。这说明了堆内存中同一个类的Class对象有且仅有一个的前提是同一个命名空间中。

为什么Student cannot be cast to Student

因为Class对象不一样,JVM会认为这是两个完全不同的引用类型,所以就会引发类型转换异常。

结尾

学习这部分内容的时候上网查阅了很多资料,参考了不少的帖子,以上是自己整理后的一些理解和总结,如有错误,请各位指出纠正。

JVM类加载器命名空间的详细总结相关推荐

  1. Java高并发编程详解系列-JVM类加载器

    之前的博客中提到了类加载的过程,提到了双亲委托机制,提到了关于类加载器的概念,这篇博客就来给大家分享一下什么是JVM的类加载器.通过实战的方式来了解一下类加载器器到底是什么. JVM类加载器分类    ...

  2. jvm十三:类加载器命名空间

    package com.atChina.jvm;import java.io.*;public class Test16 extends ClassLoader{private String clas ...

  3. 《Java虚拟机原理图解》5. JVM类加载器机制与类加载过程

    参考网址:http://blog.csdn.net/luanlouis/article/details/50529868 0.前言 读完本文,你将了解到: 一.为什么说Jabalpur语言是跨平台的 ...

  4. 彻底搞懂JVM类加载器:基本概念

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 作者 | 阿杜的世界 来源 | javaadu 在Java面试中,在考察完项目经验.基础技术 ...

  5. jvm:类加载器与双亲委派模型

    两个类相等需要类本身相等,并且使用同一个类加载器进行加载.这是因为每一个类加载器都拥有一个独立的类名称空间. 这里的相等,包括类的 Class 对象的 equals() 方法.isAssignable ...

  6. 自定义类加载器_jvm超详细探索自定义类加载器(值得收藏)

    原创:鲁班学院子牙老师 微信公众号搜索启明南 如果你想看懂本篇文章,需要你对类加载器有一定的了解.如JVM自带的类加载器.双亲委派.自定义类加载器.类加载每个阶段做了什么--如果你对这些知识还有夹生的 ...

  7. jvm类加载器以及双亲委派

    首先来了解几个概念: 类加载: 概念:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验--转换解析--初始化,最终形成能被java虚拟机直接使用的java类型,就是jvm的类加载机制. ...

  8. jvm运行时类加载机制_JVM体系结构:JVM类加载器和运行时数据区

    jvm运行时类加载机制 各位读者好! 在JVM系列的上一篇文章中,开发人员了解了Java虚拟机(JVM)及其体系结构. 本教程将帮助开发人员正确回答以下主题的问题: ClassLoader子系统 运行 ...

  9. JVM体系结构:JVM类加载器和运行时数据区

    各位读者好! 在JVM系列的上一篇文章中,开发人员了解了Java虚拟机(JVM)及其体系结构. 本教程将帮助开发人员正确回答以下主题的问题: ClassLoader子系统 运行时数据区 1.简介 在继 ...

  10. JVM—类加载器和双亲委派模型

    关注微信公众号:CodingTechWork,一起工作学习总结. 文章目录 引言 类加载器 类与类加载器关系 类加载器分类 启动类加载器 扩展类加载器 应用程序类加载器 双亲委派模型 介绍 工作流程 ...

最新文章

  1. 职称计算机证是继续教育的内容吗,豆腐网教你一分钟弄懂继续教育学时认定表及上传注意事项~...
  2. python 南京大学_南京大学python课程系列笔记之python基础之第一周:走进python
  3. USB2.0 设备类代码表
  4. 自定义字符串变量赋值在查询语句中使用
  5. 《JavaScript设计模式与开发实践》模式篇(5)—— 观察者模式
  6. VTK:PolyData之ConvexHull
  7. 高效运维最佳实践:如何做好On-call和事故响应?
  8. SAP Spartacus UI ConfigurableRoutesService router.resetConfig 的调用逻辑
  9. QueryPerformanceFrequency 和 QueryPerformanceCounter用法
  10. Spring Cloud Config 规范 1
  11. 子文件夹的权限统一于E盘的权限
  12. notepad++,vim驼峰命名与下划线的互相转换
  13. azure机器学习_Azure机器学习中的数据清理
  14. 一个肉夹馍引起的思考
  15. [转载] numpy.argmin 使用
  16. python文件下载学习
  17. vue项目 报sockjs.js?9be2:1606 GET http://192.168.43.226:8080/sockjs-node/info?t=1584966826465 net::ERR
  18. 深入浅出通信原理丨边缘计算阅读周
  19. java-jsoup爬虫
  20. 【MM VS价】移动平均价V标准价格S(一)

热门文章

  1. Merge into的使用详解
  2. Spring5学习笔记汇总
  3. java8之stream【一】steam的说明以及创建
  4. HealthKit开发快速入门教程之HealthKit的主要类型数据
  5. 08 Docker Swarm
  6. python模型评估方法_机器学习-浅谈模型评估的方法和指标
  7. R语言plot或ggplot2图片标题/图例上调用其他值
  8. 【字符串处理·新定义表达式(括号处理·递归)·思维】Kvalitetni--7.2测试 COCI
  9. 泛微E9单元测试配置及使用教程
  10. MVC中数据类型的基本常识和运用