JVM自带的类加载器:

其关系如下:

其中,类加载器在加载类的时候是使用了所谓的“父委托”机制。其中,除了根类加载器以外,其他的类加载器都有且只有一个父类加载器。
关于父委托机制的说明:

当生成 一个自定义的类加载器实例时,如果没有指定它的父加载器,那么系统类加载器将成为该类加载器的父类加载器
下面,自定义类加载器。自定义的类加载器必须继承java.lang.ClassLoader类

import java.io.*;
public class MyClassLoader extends ClassLoader {private String name;   //类加载器的名字private String path;   //加载类的路径private final String fileType = ".class";  //class文件的扩展名public MyClassLoader(String name){super();  //让系统类加载器成为该类加载器的父 类加载器,该句可省略不写this.name = name;}public MyClassLoader(ClassLoader parent,String name){super(parent);  //显示指定该类加载器的父 类加载器this.name = name;}@Overridepublic String toString() {return this.name;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}//实现自定义的类加载器必须重写findClass方法,否则ClassLoader类中的findClass()方法是抛出了异常@Overridepublic Class findClass(String name)throws ClassNotFoundException{byte[] data = this.loadClassData(name);return this.defineClass(name,data,0,data.length);}private byte[] loadClassData(String name){InputStream is = null;byte[] data = null;ByteArrayOutputStream baos = null;try {this.name = this.name.replace(".","\\"); //com.dream.it---->com\dream\itis = new FileInputStream(new File(path + name + fileType));int ch;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 {MyClassLoader loader1 = new MyClassLoader("loader1");loader1.setPath("d:/myapp/serverlib/");MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");  //loader1作为loader2的父 类加载器loader2.setPath("d:/myapp/clientlib");MyClassLoader loader3 = new MyClassLoader(null,"loader3");//父类加载器为null,表明其父类加载器为根类加载器loader3.setPath("d:/myapp/otherlib");test(loader2);test(loader3);}public static void test(ClassLoader cl) throws Exception {Class clazz = cl.loadClass("Sample");Object object = clazz.newInstance();}
}

附上findClass()方法的JDK说明

protected Class<?> findClass(String name) throws ClassNotFoundException
Finds the class with the specified binary name.
This method should be overridden by class loader
implementations that follow the delegation model
for loading classes, and will be invoked by the
loadClass method after checking the parent class
loader for the requested class. The default
implementation throws a ClassNotFoundException.
大致说明一下意思:通过指定的name来查找类。该方法应该被类
加载器的实现类重写,从而能够保证在加载类的时候可以遵循委托
机制模型。在loadClass()方法(该方法是由JVM调用的)中,
检查其父类加载器之后,该方法再被调用去加载请求的类。默认
该方法的实现是抛出了一个ClassNotFoundException异常。

其实,所谓的加载类,无非就是读取.class文件到内存中,所以在findClass()方法中,loadClassData()方法用于读取.class文件的数据,并返回一个字节数组。然后利用ClassLoader类的defineClass()方法将字节数组转换为Class对象。
上述自定义的类加载器loader1,loader2,loader3及JVM自带的类加载器之间的关系如下:

对于各个类加载器,系统的类加载器是从环境变量classpath中读取.class文件实现类的加载;loader1是从目录d:/myapp/serverlib/下读取.class文件;loader2是从目录d:/myapp/clientlib/下读取.class文件,loader3是从目录d:/myapp/otherlib/下读取.class文件
执行结果:

此处我们分析一下出现这种执行结果的原因:
当执行loader2.loadClass(“Sample”)时先由它上层的所有父类加载器尝试加载Sample类。loader1从D:\myapp\serverliv目录下成功加载了Sample类,所以loader1是Sample类的定义类加载器,loader1和loader2是Sample类的初始类加载器。
当执行loader3.loadClass(“Sample”)时,先由它上层的所有父类加载器尝试加载Sample类。loader3的父加载器为根类加载器,它无法加载Sample类,接着loader3从D:\myapp\otherlib目录下成功加载Sample类,所以loader3是Sample类的定义类加载器及初始类加载器。
在Sample类中主动使用了Dog类(new Dog()),当执行Sample类的构造方法中的new Dog()语句时,JVM需要先加载Dog类,到底用哪个类加载器家在呢?从上述的打印结果中可以看出,加载Sample类的loader1还加载了Dog类,JVM会用Sample类的定义类加载器去加载Dog类,加载过程中也同样采用了父亲委托机制。为了验证这一点,可以吧D:\myapp\serverlib目录下Dog.class文件删除,然后在D:\myapp\syslib目录下存放一个Dog.class文件,此时打印结果如下:

Sample:loader1
Dog:sun.misc.Launcher$AppClassLoader@1b84c92
Sample:loader3
Dog:loader3

由此可见,当由loader1加载的Sample类首次主动使用Dog类时,Dog类由系统类加载器加载,如果把D:\myapp\serverlib和D:\myapp\syslib目录下的Dog.class文件都删除,然后在D:\myapp\client目录下存放一个Dog.class文件。此时文件结构如下图所示:

当Loader1加载Sample类首次主动使用Dog类时,由于loader1及其父类加载器都无法加载Dog类,因此test(loader2)会抛出ClassNotFoundExcption.
这又是因为什么原因呢?
这又牵扯到命名空间的问题。
同一个命名空间内的类时相互可见的。
子加载器的命名空间包含所有父类加载器的命名空间,因此由子加载器加载的类能看见父类加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。由父加载器加载的类不能看见子加载器加载的类。
如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。
对于上述问题,loader1可以加载Sample类,而Dog类只能由loader2加载Dog类,loader1是Loader2的父类加载器,父加载器loader1加载的类Sample不能看见子加载器loader2加载的类Dog,所以会抛出异常。



对于上述实例中的main方法,我们不调用test方法,换成如下代码

Class clazz = loader1.loadClass("Sample");
Object obj = clazz.newInstance();
Sample sample = (Sample)obj;
System.out.println(sample.v1);

MyClassLoader类由系统类加载器加载,而Sample类由loader1类加载器加载,所以MyClassLoader类看不见Sample类。在MyClassLoader类的main方法中使用Sample类,会导致NoClassFoundError错误。
当两个不同命名空间内的类相互不可见时,可采用Java反射机制来访问对象实例的属性和方法。
将上述代码修改:

Class clazz = loader1.loadClass("Sample");
Object obj = clazz.newInstance();
Field field = clazz.getField("v1");
int v1 = field.getInt(obj);
System.out.println(v1);

此时,可以获取到对象中的v1属性值。利用反射机制,我们可以跨越这种命名空间的限制。
补充:
命名空间:

运行时包:

Java的自定义类加载器及JVM自带的类加载器之间的交互关系相关推荐

  1. 深入类加载器-类加载器作用,类缓存、类加载器的层次结构、ClassLoader类介绍、代理模式之双亲委派机制

    1.类加载器的作用 类加载器的作用是将class字节码文件加载到内存中,并将这些静态数据转化为方法区中的运行时数据结构,同时在堆中生成代表这个类的java.lang.Class对象,作为访问方法区中数 ...

  2. 【Android 逆向】类加载器 ClassLoader ( Android 的八种类加载器 | ClassLoader | BaseDexClassLoader | DexClassLoader )

    文章目录 一.Android 类加载器 1.ClassLoader 抽象类 2.BootClassLoader 3.BaseDexClassLoader 4.PathClassLoader 5.Dex ...

  3. Java类加载器详解

    Java虚拟机中的类加载有三大步骤:,链接,初始化.其中加载是指查找字节流(也就是由Java编译器生成的class文件)并据此创建类的过程,这中间我们需要借助类加载器来查找字节流. Java虚拟机默认 ...

  4. 深入理解Java类加载器:Java类加载原理解析

    http://blog.csdn.net/zhoudaxia/article/details/35824249 1 基本信息 每个开发人员对java.lang.ClassNotFoundExcetpi ...

  5. JVM系列之类加载器

    前言 上节我们介绍了类加载的时机和过程,对类加载有了个初步的认识,上节我们有不断提到一个东西:类加载器,那么什么是类加载器?又有哪些类加载器?类加载器之间的联系等等一些问题,今天我们将会围绕这些点展开 ...

  6. JVM 运行流程、类加载、垃圾回收

    一.JVM 简介 1.JVM JVM 是 Java Virtual Machine 的简称,意为 Java 虚拟机. 虚拟机是指通过软件模拟的具有完整硬件功能的.运行在一个完全隔离的环境中的完整计算机 ...

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

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

  8. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  9. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架...

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

最新文章

  1. 18年你需要了解的15个人工智能统计数据
  2. 005 定位控件输入call
  3. SpringBoot返回json和xml
  4. 学计算机的用哪种笔记本写字,平面设计笔记本电脑,学平面设计用什么电脑好...
  5. html5光标进去默认值消失,html点击input没有出现光标怎么办
  6. [010]Try块和异常处理
  7. Oracle v$sql,v$sqlarea,v$sqltext区别
  8. 免登录实现CSDN博客代码复制(2021.9.26)
  9. Vue项目中安装axios
  10. ubuntu18.04 linux journalctl 命令
  11. 小学计算机面试说课稿,小学信息技术面试说课稿(模板)
  12. HTML+CSS+JS斗地主 记录局数 可自定义昵称
  13. 机器学习sklearn基础(1):多元逻辑回归分类器 (pcolormesh说明及绘图)
  14. 漫话:如何给女朋友解释灭霸的指响并不是真随机消灭半数宇宙人口的?
  15. word表格内文字行间距调整方法
  16. dedeCMS自定义dede标签
  17. IT硬件运维的一些建议
  18. Android 字体库的使用。引入外部字体
  19. 导图解房(01)黄金圈法则解读 买房这件事儿
  20. ios 长按 放大镜_如何在iOS 10中将iPhone的相机用作放大镜

热门文章

  1. GPS快速定位之----AGPS、EPO
  2. 常见的量化交易策略和源码编写过程分享
  3. CPU性能指标和分析工具
  4. multisim 四位数码管_multisim中数码管 DCD-HEX 四个引脚都是指的什么?
  5. 对一个项目如何写一个方案?
  6. java权限管理+授权与认证_权限管理(认证和授权)
  7. 创业开始_我的锦誉德工作室成立
  8. 制作initrd镜像文件
  9. L1-008 求整数段和(Python3)
  10. 网易云音乐热评详细源码-Python