即将类从磁盘加载到Java虚拟机内存区中。

流程

验证-> 准备 -> 解析 -> 初始化

验证

验证就是验证字节码文件的格式是否正确。

准备

为静态变量分配内存并且赋默认值,例如int类型赋值0.

解析

将 符号引用替换为直接引用,将一些静态方法如main()方法替换为所在的内存的指针(直接引用),这就是静态链接的过程。该过程在程序运行期间完成的。

初始化

将类的静态变量初始化为指定的值,执行静态代码快。例如将某个int类型的值赋值为666。注意和准备阶段的区分,准备阶段是赋予默认值,例如0, null 等。

注意

  1. 只有当涉及到类的初始化的时候,才会去加载类,仅定义一个引用不会加载,即:
    1. B b = null;
    以上代码,不会加载B这个类,那么也不会去执行B中的静态代码块。

类加载器和双亲委派机制

类加载器

类加载器的本质仍然是类, 类加载过程主要通过类加载器来实现。类型:

  1. 引导类加载器:加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar, charsets.jar.
  2. 扩展类加载器:加载支撑JVM运行的位于JRE的lib目录下的ext目录下的JAR类包;
  3. 应用程序类加载器:说白了就是加载我们自己写的类,位于ClassPath路径下的类包;
  4. 自定义加载器:

双亲委派机制

即先向上委托上层加载器去加载,加载不到的时候才让下层加载器去加载。

1627457363(1).png
PS: 并不是说扩展类加载器是应用程序类加载器的父类,只是说这两个加载器在结构上存在着亲子关系。

那么为什么需要双亲委派机制?

  1. 沙箱安全机制:例如自己写了一个String类,包名类名都和JDK自带的String类一致,那么自己的String类不会被加载,因为会一直委托父加载器去加载,父加载器可以成功找到String类,那么自己的这个String类就不会被加载了。
  2. 避免类的重复加载:如果父类加载器已经加载过某个类了,子加载器

最核心代码

findClass方法:根据类名,转化成对应的路径名,从磁盘中读取对应路径的类文件,加载进来。

如何打破双亲委派机制?

打破双亲委派机制指的是,当使用自定义加载器去加载类时,不再向上委托。
实现方式:
自定义加载器中重写LoadClass方法,删除掉双亲委派那部分代码。但仅仅这样说不够的,因为自己写的类要继承object类,该类是JAVA中的核心类,需要通过引导类加载器加载,但是目前使用的是自定义加载器,那么在指定的目录下没有object类,所以加载不到。

错误解决方式:
将object拷贝一份到指定的目录下,尝试通过自定义加载器加载object类。

错误原因:
JAVA给核心类加了沙盒保护机制,不允许用户通过自定义加载器加载这些核心类,以防止被重写导致双亲委派机制失去意义。

正确解决方式:
重写loadClass方法时,加一次判断,当加载我们自己的类时,打破双亲委派机制,否则调用parent的loadClass方法:

  1. c = this.getParent().loadClass(name);