类加载-java

注意区分类加载和加载阶段的区别

类加载:指整个类加载过程,包括:加载阶段、链接阶段、初始化阶段。

加载阶段:指类的加载过程中的一个阶段(加载阶段)。

类生命周期

加载

加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,任何类都有且仅有一个自己的class对象,也就是我们反射获取的那个class对象。

类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器

1、从本地文件系统加载class文件,这是绝大部分程序的类加载方式。
2、从JAR包加载class文件,这种方式也是很常见的,JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。
3、通过网络加载class文件。
4、把一个Java源文件动态编译,并执行加载

链接

分为验证、准备和解析三步,在链接的准备阶段会为类的静态变量分配内存,并设置默认初始值,例如代码中一个静态变量private static int a = 10,在这一步a变量会被分配内存空间并且获得0的初始值,此时a=0.

初始化

初始化是为类的静态变量赋予正确的初始值,例如链接阶段的准备给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析,到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

使用

卸载

类加载执行代码

初始化:调用静态代码块

实例化:构造代码块,构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Person {
public String name;
public int age;
public static int id;

static{
System.out.println("静态代码块");
}
public static void staticAction(){
System.out.println("静态方法");
}

{
System.out.println("构造代码块");//任意构造方法执行都会调用构造代码块
}

public Person() {
System.out.println("无参构造");
}

public Person(String name,int age) {
System.out.println("有参构造");
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

}

使用阶段

image-20230323094157496

image-20230323094543964

调用类静态方法

image-20230323094655878

给类静态变量赋值

image-20230323094802322

加载类不进行初始化

image-20230323094922650

类加载方式

隐式加载

创建类对象
使用类的静态域
创建子类对象
使用子类的静态域
在JVM启动时,BootStrapLoader会加载一些JVM自身运行所需的class
在JVM启动时,ExtClassLoader会加载指定目录下一些特殊的class
在JVM启动时,AppClassLoader会加载classpath路径下的class,以及main函数所在的类的class文件

显式加载

ClassLoader.loadClass(className)

​ 只加载和连接、不会进行初始化。

Class.forName(String name, boolean initialize,ClassLoader loader)

​ 使用loader进行加载和连接,根据参数initialize决定是否初始化。

类初始化

执行静态代码块

Class.forName默认是进行初始化的,我们可以调用第二个重载,不进行初始化

image-20230323100312090

子类初始化之前会先初始化父类

image-20230323101128934