【JAVA】类加载在什么时候发生

类加载

每个类的类加载有且只发生一次

类加载: 就是 JVM 把 .class 文件读到内存里,并完成静态字段(成员变量)初始化和静态代码块执行,让这个类准备好被使用

有一下几种情况会触发类加载

  • new一个新对象
  • 调用类的静态成员变量(静态字段)或静态代码块
  • 子类被调用要先加载父类

懒加载: 如果在程序一开始运行的时候就把所有的类都全部加载一遍, 这太耗费资源了, JVM 对于类加载是能拖就拖, 直到出现了上述的情况让他不得不进行类加载


加载顺序

加载的顺序如下表所示

步骤 内容 时机
1 父类静态字段 + 父类静态代码块 类加载时,只执行一次
2 子类静态字段 + 子类静态代码块 类加载时,只执行一次
3 父类实例字段 + 父类实例代码块 每次 new 对象时
4 父类构造方法 每次 new 对象时
5 子类实例字段 + 子类实例代码块 每次 new 对象时
6 子类构造方法 每次 new 对象时

举个例子

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class Parent {
// ===== 静态部分(类加载时执行,全程序只一次)=====
static int parentStatic = print("1. Parent 静态字段", 100);

static {
System.out.println("2. Parent 静态代码块");
}

// ===== 实例部分(每次 new 时执行)=====
int parentField = print("4. Parent 实例字段", 10);

{
System.out.println("5. Parent 实例代码块");
}

public Parent() {
System.out.println("6. Parent 构造方法");
}

// ===== 方法(被调用时才执行)=====
public void hi() {
System.out.println(">> Parent.hi() 被调用");
}

public static void hello() {
System.out.println(">> Parent.hello() 静态方法被调用");
}

static int print(String msg, int val) {
System.out.println(msg + " = " + val);
return val;
}
}

class Child extends Parent {
static int childStatic = print("3. Child 静态字段", 200);

static {
System.out.println("3.5 Child 静态代码块");
}

int childField = print("7. Child 实例字段", 20);

{
System.out.println("8. Child 实例代码块");
}

public Child() {
System.out.println("9. Child 构造方法");
}

@Override
public void hi() {
System.out.println(">> Child.hi() 被调用(重写)");
}
}

class Unused {
static {
System.out.println("!!! Unused 被加载了 !!!");
}
}

public class Main {
public static void main(String[] args) {
System.out.println("==== 程序开始 ====\n");

System.out.println("---- 步骤 1:第一次 new Child() ----");
Child c1 = new Child();

System.out.println("\n---- 步骤 2:第二次 new Child() ----");
Child c2 = new Child();

System.out.println("\n---- 步骤 3:调用实例方法(多态)----");
Parent p = c1;
p.hi();

System.out.println("\n---- 步骤 4:调用静态方法 ----");
Parent.hello();

System.out.println("\n==== 程序结束 ====");
System.out.println("(Unused 类自始至终没被加载)");
}
}

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
==== 程序开始 ====

---- 步骤 1:第一次 new Child() ----
1. Parent 静态字段 = 100
2. Parent 静态代码块
3. Child 静态字段 = 200
3.5 Child 静态代码块
4. Parent 实例字段 = 10
5. Parent 实例代码块
6. Parent 构造方法
7. Child 实例字段 = 20
8. Child 实例代码块
9. Child 构造方法

---- 步骤 2:第二次 new Child() ----
4. Parent 实例字段 = 10
5. Parent 实例代码块
6. Parent 构造方法
7. Child 实例字段 = 20
8. Child 实例代码块
9. Child 构造方法

---- 步骤 3:调用实例方法(多态)----
>> Child.hi() 被调用(重写)

---- 步骤 4:调用静态方法 ----
>> Parent.hello() 静态方法被调用

==== 程序结束 ====
(Unused 类自始至终没被加载)

总结: JVM 在最晚的时机(第一次用到)加载这个类,加载之后再也不重复。