知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方可以评论,我们一起探讨!
类加载阶段
Java 程序在运行时,JVM 会将类加载到内存中,类加载过程包括加载、验证、准备、解析和初始化五个阶段,其中与执行顺序密切相关的是准备和初始化阶段。
- 准备阶段:为类的静态变量分配内存,并将其初始化为默认值。例如,
int类型的静态变量初始化为 0,boolean类型的初始化为false,引用类型初始化为null等。 - 初始化阶段:执行类的静态代码块和静态变量的显式赋值。静态代码块和静态变量的初始化顺序是按照它们在类中出现的先后顺序执行的。
举例代码及分析
public class ClassExecutionOrder {
// 静态变量
public static int staticVariable = 1;
// 静态代码块
static {
System.out.println("静态代码块执行,staticVariable 的值为: " + staticVariable);
staticVariable = 2;
}
// 实例变量
public int instanceVariable = 3;
// 实例代码块
{
System.out.println("实例代码块执行,instanceVariable 的值为: " + instanceVariable);
instanceVariable = 4;
}
// 构造方法
public ClassExecutionOrder() {
System.out.println("构造方法执行,instanceVariable 的值为: " + instanceVariable);
}
public static void main(String[] args) {
System.out.println("main 方法开始");
// 创建对象
ClassExecutionOrder obj = new ClassExecutionOrder();
System.out.println("main 方法结束");
}
}
执行顺序详细分析
-
静态成员初始化:
- 当 JVM 启动并执行
main方法时,首先会加载ClassExecutionOrder类。在类的加载过程中,会先执行准备阶段,为静态变量staticVariable分配内存并初始化为默认值 0。 - 接着进入初始化阶段,按照静态成员在类中出现的顺序执行。首先对
staticVariable进行显式赋值为 1,然后执行静态代码块,输出静态代码块执行,staticVariable 的值为: 1,并将staticVariable的值修改为 2。
- 当 JVM 启动并执行
-
main方法执行:输出main 方法开始。 -
实例成员初始化:
- 当执行
new ClassExecutionOrder()时,会创建ClassExecutionOrder类的一个实例对象。在创建对象之前,会先为实例变量分配内存并初始化为默认值。这里instanceVariable被初始化为 0。 - 然后按照实例成员在类中出现的顺序执行。首先执行实例代码块,输出
实例代码块执行,instanceVariable 的值为: 3,并将instanceVariable的值修改为 4。 - 最后执行构造方法,输出
构造方法执行,instanceVariable 的值为: 4。
- 当执行
-
main方法结束:输出main 方法结束。
存在继承关系时的执行顺序
当存在继承关系时,执行顺序会更加复杂。以下是一个示例代码:
class Parent {
public static int parentStaticVariable = 1;
static {
System.out.println("父类静态代码块执行,parentStaticVariable 的值为: " + parentStaticVariable);
parentStaticVariable = 2;
}
public int parentInstanceVariable = 3;
{
System.out.println("父类实例代码块执行,parentInstanceVariable 的值为: " + parentInstanceVariable);
parentInstanceVariable = 4;
}
public Parent() {
System.out.println("父类构造方法执行,parentInstanceVariable 的值为: " + parentInstanceVariable);
}
}
class Child extends Parent {
public static int childStaticVariable = 5;
static {
System.out.println("子类静态代码块执行,childStaticVariable 的值为: " + childStaticVariable);
childStaticVariable = 6;
}
public int childInstanceVariable = 7;
{
System.out.println("子类实例代码块执行,childInstanceVariable 的值为: " + childInstanceVariable);
childInstanceVariable = 8;
}
public Child() {
System.out.println("子类构造方法执行,childInstanceVariable 的值为: " + childInstanceVariable);
}
public static void main(String[] args) {
System.out.println("main 方法开始");
Child child = new Child();
System.out.println("main 方法结束");
}
}
存在继承关系时的执行顺序分析
-
父类静态成员初始化:
- 加载
Child类时,由于Child类继承自Parent类,JVM 会先加载Parent类。在Parent类的准备阶段,为parentStaticVariable分配内存并初始化为 0。 - 进入
Parent类的初始化阶段,先对parentStaticVariable显式赋值为 1,然后执行Parent类的静态代码块,输出父类静态代码块执行,parentStaticVariable 的值为: 1,并将parentStaticVariable的值修改为 2。
- 加载
-
子类静态成员初始化:
- 接着对
Child类进行准备阶段,为childStaticVariable分配内存并初始化为 0。 - 进入
Child类的初始化阶段,先对childStaticVariable显式赋值为 5,然后执行Child类的静态代码块,输出子类静态代码块执行,childStaticVariable 的值为: 5,并将childStaticVariable的值修改为 6。
- 接着对
-
main方法执行:输出main 方法开始。 -
父类实例成员初始化:
- 创建
Child对象时,会先调用父类的实例初始化过程。为parentInstanceVariable分配内存并初始化为 0。 - 执行
Parent类的实例代码块,输出父类实例代码块执行,parentInstanceVariable 的值为: 3,并将parentInstanceVariable的值修改为 4。 - 执行
Parent类的构造方法,输出父类构造方法执行,parentInstanceVariable 的值为: 4。
- 创建
-
子类实例成员初始化:
- 为
childInstanceVariable分配内存并初始化为 0。 - 执行
Child类的实例代码块,输出子类实例代码块执行,childInstanceVariable 的值为: 7,并将childInstanceVariable的值修改为 8。 - 执行
Child类的构造方法,输出子类构造方法执行,childInstanceVariable 的值为: 8。
- 为
-
main方法结束:输出main 方法结束。
总结
- 无继承关系时,执行顺序为:静态变量初始化 -> 静态代码块 ->
main方法 -> 实例变量初始化 -> 实例代码块 -> 构造方法。 - 存在继承关系时,执行顺序为:父类静态变量初始化 -> 父类静态代码块 -> 子类静态变量初始化 -> 子类静态代码块 ->
main方法 -> 父类实例变量初始化 -> 父类实例代码块 -> 父类构造方法 -> 子类实例变量初始化 -> 子类实例代码块 -> 子类构造方法。