最近在做一个热加载Class的小组件,这个组件需要对类加载器ClassLoader有所了解,我就顺便借这个机会把学到的一点皮毛与大家分享一下。

从Class文件开始

ClassLoader,顾名思义就是类加载器。简单的说就是把Class文件加载到JVM中,之后程序就能正常的运行了。

我们平时写的代码都是.java格式的文件,但是这个文件是不能够直接运行的。比如下面这个简单的测试程序Test.java

 public class Test {

     public static void main(String[] args) {
System.out.println("Testing");
}
}

在文件夹下看,就是这么一个文件:

我们在命令行下编译一下这个Java文件:

 javac Test.java 

然后就可以在目录下看到生成的class文件,也就是JVM可以识别运行的文件。

Java自带的类加载器

Java自带有三个类加载器:

Bootstrap ClassLoader:主要加载核心类库,%Java_HOME%\lib 下的rt.jar、resources.jar、charsets.jar和class等,总之就是jdk安装目录下lib目录下的一些核心jar包。

Extention ClassLoader 扩展的类加载器,主要加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件,就是JRE安装目录下lib目录下ext目录下的jar和class文件。

Appclass Loader也称为SystemAppClass应用程序加载器 加载当前应用的classpath的所有类,这个就是很好理解了,就是我们平时自己写的项目类路径下的类。

双亲委派机制

接下来我先祭出一张常见的图:

双亲委派机制的意思是:一个类加载器遇到一个加载类的请求,首先不会自己去加载,而是看自己的父加载器能否加载这个类,然后一级一级往上传直到遇到能够加载这个类的加载器。

简单可以形容为一家几口吃苹果:
小明:爸爸,这苹果你吃吗?
小明的爸爸:我先问问你爷爷,爸,这苹果你吃吗?
小明的爷爷:你们真孝顺,那我就恭敬不如从命啦。

如果遇到顶层的父类加载器无法加载,该怎么办?这个时候,就往下找,找到第一个能加载这个类的加载器
小明:爸爸,这苹果你吃吗?
小明的爸爸:我先问问你爷爷,爸,这苹果你吃吗?
小明的爷爷:牙不好啦,我不吃啦你们吃吧。
小明的爸爸:好的,我的牙口还不错,那我就吃了。

那么为什么需要一个机制呢,原因主要是为了避免类加载程序的混乱。

比如Java官方指定了一个java.lang.String类,然后你自己又重新写了一个java.lang.String类的类。这个时候,你想import一个String类型,就没有办法判断到底这个String是哪个String了。

当然这边要多说一句,父类加载器不等于父类,也就是上面的三个类加载器并不是继承关系。

自定义ClassLoader

刚刚那张图大家应该已经发现了,除了三个系统自带的ClassLoader以外,最底层还有几个自定义的类加载器。

没错,原来的类加载器只是按照特定的方式加载指定目录下的jar包,那么如果你想按照自己的要求加载一些东西呢?这就需要你自己去定义一个classloader了。

比如以我为例,我希望可以多次重新加载同一个Class。

ClassLoader中,加载Class的方法是loadClass(),我们可以看一下源码:

    protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
} if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name); // this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

可是在ClassLoader中的加载class的方法loadClass()中,有一个  findLoadedClass() 方法来判断Class是否已经被加载的操作,不符合要求,而且loadClass()方法中也会按照双亲委派去找寻父类加载器,不符合要求。(看不懂源码没有关系,里面的英文还是可以看懂的嘛哈哈)

于是我的步骤

  1. 编写一个类继承自ClassLoader抽象类。
  2. 复写它的findClass()方法,直接调用findClass()来找寻Class文件,而不是loadClass()。
  3. findClass()方法中调用defineClass(),defineClass()将class二进制内容转换成Class对象并加载进内存

这样子,就实现了我的重复加载Class的目的。

类加载器ClassLoader的理解的更多相关文章

  1. 深入理解Java类加载器(ClassLoader) (转)

    转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...

  2. 深入理解Java类加载器(ClassLoader)

    深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...

  3. Java虚拟机学习(5):类加载器(ClassLoader

    类加载器 类加载器(ClassLoader)用来加载 class字节码到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源文件在经过 Javac之后就被转换成 ...

  4. jvm之java类加载机制和类加载器(ClassLoader),方法区结构,堆中实例对象结构的详解

    一.类加载或类初始化:当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载.连接.初始化3个步骤来对该类进行初始化.如果没有意外,JVM将会连续完成3个步骤. 二.类加载时机:  1 ...

  5. Java 类加载器(ClassLoader)

    类加载器 ClassLoader 什么是类加载器? 通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代 ...

  6. 类加载器ClassLoader

    上篇文章说到,Class类可以通过一个类的全限定名去加载类,那么底层是如何去加载的呢?这就是我们今天要聊的类加载器ClassLoader,其可以通过一个类的全限定名来获取描述此类的二进制字节流,也即是 ...

  7. 类加载器ClassLoader之jar包隔离

    小引子 最近做了一个根据同一模块的不同jar版本做同时测试的工具,感觉挺有意思,特此记录. 类加载器(ClassLoader)是啥? 把类加载阶段中的"通过一个类的全限定名(博主注:绝对路径 ...

  8. Java类加载器ClassLoader总结

    JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中. 2.显式装载, 通过class.forname()等方法,显 ...

  9. Java类加载器(ClassLoader)

    类加载的机制的层次结构 每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Jav ...

随机推荐

  1. Java 进制转换(二进制(负),八进制,十进制,十六进制),位运算、逻辑运算(2)

    负数的二进制表现形式:其实就是该数的绝对值取反+1. 进制转换(二进制,八进制,十进制,十六进制),原理解析 十六进制的表现形式: (2)(与.异或.左移.右移.三元运算符)

  2. 深入分析Java反射(二)-数组和枚举

    前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...

  3. 01 语言基础+高级:1-4 接口与多态_day09【继承、super、this、抽象类】

    day09[继承.super.this.抽象类] 三大特性——继承方法重写super关键字this关键字抽象类 教学目标能够解释类名作为参数和返回值类型能够写出类的继承格式能够说出继承的特点能够说出子 ...

  4. Vue专题-生命周期

    有时候,我们需要在实例创建过程中进行一些初始化的工作,以帮助我们完成项目中更复杂更丰富的需求开发,针对这样的需求,Vue提供给我们一系列的钩子函数. 本文详细介绍了Vue实例在创建和销毁的过程中,我们 ...

  5. python中的变量对象小结2

    # .变量名和数据内容是分开存储的. # .数据保存在内存中的一个位置(地址). # .变量中保存着数据在内存中的地址. # 引用就是变量中记录数据的地址. #不可变变量,重新赋值时会重新开辟一个地址 ...

  6. Python笔记_第一篇_面向过程_第一部分_8.画图工具(小海龟turtle)

    turtle 是一个简单的绘图工具. 提供一个小海龟,可以把它理解为一个机器人,只能听懂有限的命令,且绘图窗口的原点(0,0)在中间,默认海龟的方向是右侧海龟的命令包括三类:运动命令.笔画控制命令.其 ...

  7. typescript-学习使用ts-2

    解构赋值 数组解构 let input = [1, 2]; let [first, second] = input; console.log(first); // outputs 1 console. ...

  8. 当年写的C代码

    #ifndef KMIN_H_ #define KMIN_H_ /******************************************************************* ...

  9. 嵌入式c语言编码规范

    学习嵌入式的同学应该首先掌握嵌入式编码规范,这样才能更好的嵌入式系统. 下面就从这几个方面讲解一下嵌入式c编码规范. 注释风格.排版风格.头文件风格.变量定义.宏定义.函数 1 注释风格 1.1  注 ...

  10. MyBatis从入门到精通(第4章):MyBatis动态SQL【if、choose 和 where、set、trim】

    (第4章):MyBatis动态SQL[if.choose 和 where.set.trim] MyBatis 的强大特性之一便是它的动态 SQL.MyBatis 3.4.6版本采用了功能强大的OGNL ...