一、创建Sample

1、创建实例

public class MyPerson {

    private MyPerson myPerson;

    public void  setMyPerson(Object obj){
this.myPerson = (MyPerson)obj;
}
}

  

2、创建测试类

public class MyTest20 {

    public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
MyTest16 loader2 = new MyTest16("loader2"); Class<?> clazz1 = loader1.loadClass("com.example.jvm.classloader.MyPerson"); Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyPerson"); System.out.println( clazz1 == clazz2); Object object1 = clazz1.newInstance();
Object object2 = clazz2.newInstance(); Method method = clazz1.getMethod("setMyPerson", Object.class);
method.invoke(object1, object2); }
}

  

3、MyTest16类和之前的一致

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; } }

打印结果

true

  

二、修改Sample

在一的基础上修改代码,设置path

loader1.setPath("D:/temp/");

loader2.setPath("D:/temp/");

public class MyTest21 {

    public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
MyTest16 loader2 = new MyTest16("loader2"); loader1.setPath("D:/temp/");
loader2.setPath("D:/temp/"); Class<?> clazz1 = loader1.loadClass("com.example.jvm.classloader.MyPerson"); Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyPerson"); System.out.println( clazz1 == clazz2); Object object1 = clazz1.newInstance();
Object object2 = clazz2.newInstance(); Method method = clazz1.getMethod("setMyPerson", Object.class);
method.invoke(object1, object2); }
}

  然后将class文件所在的build下的com文件夹拷贝到D:\temp 下,删除build下的MyPerson.class  文件

打印结果:

findClass invoked:com.example.jvm.classloader.MyPerson
class loader name: loader1
findClass invoked:com.example.jvm.classloader.MyPerson
class loader name: loader2
false
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.example.jvm.classloader.MyTest21.main(MyTest21.java:27)
Caused by: java.lang.ClassCastException: com.example.jvm.classloader.MyPerson cannot be cast to com.example.jvm.classloader.MyPerson
at com.example.jvm.classloader.MyPerson.setMyPerson(MyPerson.java:11)
... 5 more

  结果分析,loader1和loader2分别加载了MyPerson,分别给MyPerson分配了内存空间,如下图:

loader1和loader2是两个不同的命名空间。

所以System.out.println( clazz1 == clazz2);的结果为false

这里可以回顾下命名空间的概念

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

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

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

不同类加载器的命名空间关系

同一个命名空间内的类是相互可见的

子加载器的命名空间包含所有父加载器的命名空间。因此由子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。

由父加载器加载的类不能看见子加载器加载的类。

如果两个类加载器之间没有直接或间接的父子关系,那么他们各自加载的类相互不可见。

所以上面抛出异常的原因为:如果两个类加载器之间没有直接或间接的父子关系,那么他们各自加载的类相互不可见

三、类加载器的双亲委托模型的好处

1、可以确保Java核心库的类型安全: 所有的Java应用都至少会引用java.lang.Object类,也就是说在运行期,java.lang.Object这个类

会被加载到Java虚拟机中; 如果这个加载过程是由Java应用自己的类加载器所完成的,那么很可能就会 在JVM中存在多个版本的
java.lang.Object类,而且这些类之间还是不兼容的,相互不可见的。(正是命名空间在发挥作用 )
借助于双亲委托机制,Java核心类库中的类的加载工作都是由启动类加载器来统一完成。从而确保了Java应用所使用的都是同一个版本的
Java核心类库,他们之间是相互兼容的。
2、可以确保Java核心类库所提供的类不会被自定义的类所替代。
3、不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间。相同名称的类可以并处在Java虚拟机中,只需要不同的类加载器来加载
他们即可。不同类加载器所加载的类之间是不兼容的,这就相当于在Java虚拟机内部创建一个又一个相互隔离的Java类空间。这类技术在很多框架中
都得到了应用。

JVM 类加载器命名空间深度解析与实例分析的更多相关文章

  1. (二十七)JVM类加载器机制与类加载过程

    一.Java虚拟机启动.加载类过程分析 下面我将定义一个非常简单的java程序并运行它,来逐步分析java虚拟机启动的过程. package org.luanlouis.jvm.load; impor ...

  2. 深入JVM类加载器机制,值得你收藏

    先来一道题,试试水平 public static void main(String[] args) { ClassLoader c1 = ClassloaderStudy.class.getClass ...

  3. JVM类加载器的分类

    类加载器的分类 JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader). 从概念上来讲,自定 ...

  4. JVM 类加载器深入解析以及重要特性剖析

    1.类加载流程图 从磁盘加载到销毁的完整过程. 2.类加载流程图2 1.加载: 就是把二进制形式的java类型读入java虚拟机中 2.连接: 验证.准备.解析. 连接就是将已经读入到内存的类的二进制 ...

  5. JVM 类加载器 (二)

    1.类加载器(ClassLoader)负责加载class文件,class文件在文件开头有特定的文件标识,并且ClassLoader只负责 class 文件的加载,至于class文件是否能够运行则由Ex ...

  6. JVM类加载器及Java类的生命周期

    预定义类加载器(三种): 启动(Bootstrap)类加载器: 是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar) ...

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

    本文阅读时间大约9分钟. 写在前面 在Java面试中,在考察完项目经验.基础技术后,我会根据候选人的特点进行知识深度的考察,如果候选人简历上有写JVM(Java虚拟机)相关的东西,那么我常常会问一些J ...

  8. 从 1 开始学 JVM 系列 | JVM 类加载器(一)

    从 1 开始学 JVM 系列 类加载器,对于很多人来说并不陌生.我自己第一次听到这个概念时觉得有点"高大上",觉得只有深入 JDK 源码才会触碰到 ClassLoader,平时都是 ...

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

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

随机推荐

  1. 【Redis】基本数据类型及命令操作(超详细)

    一.String 1.1 概述 1.2 相关命令列表 1.3 命令示例 二.List 2.1 概述: 2.2 相关命令列表: 2.3 命令示例: 2.4 链表结构的小技巧: 三.Hashes 3.1 ...

  2. 尚硅谷韩顺平Linux教程学习笔记

    目录 尚硅谷韩顺平Linux教程学习笔记 写在前面 虚拟机 Linux目录结构 远程登录Linux系统 vi和vim编辑器 关机.重启和用户登录注销 用户管理 实用指令 组管理和权限管理 定时任务调度 ...

  3. Spring Cloud原理详解

    概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...

  4. 写Shell脚本自动生成首行

    送给经常写shell脚本的兄弟们常写shell脚本的时候,大家一定都有困扰,怎么样能让.sh文件的表头自己生成,不用我们自己去敲呢 首先我们要编写一下/etc/vimrc执行 vim /etc/vim ...

  5. 一:MySQL系列之基本介绍(一)

    本篇主要介绍关于MySQL数据的基本知识,包括数据存储的变化,什么是MySQL以及其有什么优点.以及什么是RDBMS概念性知识等,以及关于MySQL语句的SOL的基本用法: 一.数据库 数据库,顾名思 ...

  6. k8s集群之上运行etcd集群

    一.知识点: 1.headless services NOTE:: 我们在k8s上运行etcd集群,集群间通告身份是使用dns,不能使用pod ip,因为如果pod被重构了ip会变,在这种场景中不能直 ...

  7. 快速搭建Kerberos服务端及入门使用

    快速搭建Kerberos服务端及入门使用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Kerberos是一种网络身份验证协议.它旨在通过使用秘密密钥加密为客户端/服务器应用程序提 ...

  8. MySQL学习总结 (InnoDB)

    主要内容: 存储结构 索引 锁 事务 存储结构 表 索引组织表:表是根据主键顺序组织存放的.如果表中没有非空惟一索引,引擎会自动创建一个6字节大小的指针. 主键的索引是定义索引的顺序,而不是建表时列的 ...

  9. java获取本机外网ip

    public static String getV4IP(){ String ip = ""; String chinaz = "http://ip.chinaz.com ...

  10. 测试工具( Burp Suite)介绍了解篇

    Mac 安装 Burp Suite破解版,参考链接: https://www.jianshu.com/p/3224c2308ffa 建议:目前官网的最新版为2.1.4.建议使用1.7.36版本,有破解 ...