目录
- 1- 引言:类加载器
- 1-1 类加载器是什么?(What)
- 1-2 为什么要用类加载器? 作用:类加载的过程?(Why)
- 2- ⭐核心:类加载器详解(How)
- 2-1 类加载器分类
- 2-2 什么是双亲委派模型?
- 2-3 为什么采用双亲委派机制?
- 3- 小结:
- 3-1 类加载器是什么?类加载器有哪些种类?
- 3-2 什么是双亲委派模型?为什么采用双亲委派模型?
1- 引言:类加载器
1-1 类加载器是什么?(What)
- 类加载器:JVM 只会运行二进制文件,类加载器的作用就是将字节码文件(.class文件)加载到JVM中,从而让 Java 程序能够启动起来。它读取二进制数据,并将这些数据转换成
java.lang.Class
类的实例。每个这样的实例用于表示 JVM 中的一个 Java 类。
1-2 为什么要用类加载器? 作用:类加载的过程?(Why)
1-加载(Loading):
- 类加载器从各种来源(如文件系统、网络等)读取特定的类文件(.class文件),并为之创建一个
java.lang.Class
对象。在这个阶段,JVM会获取类的全名、类中的直接超类、是否为接口、方法、字段和常量池等基本信息。
2-验证(Verification):
- 验证阶段确保加载的类符合JVM规范,并检查字节码文件是否未被篡改且安全无害。这一步骤包括确保格式正确,检查语义(例如,final的类没有子类),并确保字节码不包含非法数据。
3-准备(Preparation):
- 在准备阶段,JVM为类变量分配内存,并设置默认初始值,这些变量使用的内存必须在方法区中进行分配。此阶段不包括Java代码赋予的具体值,而是系统设定的默认值(如null、0或false)。
4-解析(Resolution):
- 解析是将类、接口、字段和方法的符号引用转换为直接引用的过程。这一步骤涉及到将在类的常量池内的符号引用替换为实际引用的具体过程。
5-初始化(Initialization):
- 初始化是类加载过程的最后一个阶段,涉及执行类构造器
<clinit>()
方法的过程。此方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生。这一步是执行Java程序代码初始化类变量和静态代码块。
2- ⭐核心:类加载器详解(How)
2-1 类加载器分类
①启动类加载器/引导类加载器(BootStrap ClassLoader)
- 该类加载器是使用 C++ 语言编写的,主要是用来加载 Java 核心库的。
②扩展类加载器(ExtClassLoader)
- 实际上是加载扩展目录的,加载 jre/lib/ext 下的扩展目录的内容
③应用类加载器(AppClassLoader)
- 应用类加载器,AppClassLoader 主要负责加载的是 CLASSPATH 下的所有类,是 Java 默认的类加载器,负责加载开发者自己编写的 Java 类。
- 在 Java 的日常应用开发中,类似加载基本上都是由这三种互相配合来去完成加载的,各自加载各自的类即可。
④自定义类加载器(CustomerClassLoader)
- 自定义类加载器,实现自定义加载功能在实际的日常开发中应用的不是很多。
2-2 什么是双亲委派模型?
- 双亲委派模型:在Java虚拟机(JVM)加载类时,它采用了一种称为双亲委派模型的机制。具体来说,当JVM需要加载一个特定的类时,当前的类加载器会首先委托其父类加载器尝试加载这个类。这个过程会一直向上递归,即每个类加载器都会先委托其父类加载器,直到达到顶层的启动类加载器。如果父类加载器能成功加载该类,则使用父类加载器的定义;如果父类加载器无法加载该类(即不在其负责的路径下找到该类的字节码),那么子加载器会尝试自己加载这个类。这种模型的主要优势在于它能够确保Java核心库的类不被自定义的类所替代,从而增强了Java应用的安全性和类的一致性。
2-3 为什么采用双亲委派机制?
- ① 通过双亲委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,保持一致性。
- ② 为了安全,保证类库 API 不会被修改。
举例:
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("demo info");
}
}
- 执行上述代码的 main 函数,会出现异常,在类 java.lang.String 中找不到 main 方法
原因:由于是双亲委派的机制,java.lang.String
的在启动类加载器得到加载,因为在核心jre
库中有其相同名字的类文件,但该类中并没有 main
方法。这样就能防止恶意篡改核心 API
库。