【Java】 初始化对象顺序

在Java中,对象的初始化顺序遵循以下一般规则,按照它们被声明和定义的顺序来进行:

  1. 父类静态内容 (如果有继承发生):

    • 静态变量声明和静态初始化块按照它们在父类中出现的顺序执行。
  2. 子类静态内容:

    • 静态变量声明和静态初始化块按照它们在子类中出现的顺序执行。
  3. 父类非静态内容 (如果有继承发生):

    • 父类的非静态变量声明和非静态初始化块按照它们在父类中出现的顺序执行。
    • 父类的构造函数执行。
  4. 子类非静态内容:

    • 子类的非静态变量声明和非静态初始化块按照它们在子类中出现的顺序执行。
    • 子类的构造函数执行。

具体步骤如下:

  • 静态变量和静态初始化块

    静态变量和静态初始化块按照它们在类中的顺序进行初始化,这个过程只会在类加载时发生一次。

  • 实例变量和实例初始化块

    实例变量和实例初始化块按照它们在类中的顺序初始化,这个过程会在每次创建类的实例时发生。

  • 构造函数

    在实例变量和实例初始化块初始化之后,构造函数被执行。

举一个具体的例子:

csharp 复制代码
public class Parent {
    static {
        System.out.println("Parent static block");
    }

    {
        System.out.println("Parent instance block");
    }

    public Parent() {
        System.out.println("Parent constructor");
    }
}

public class Child extends Parent {
    static {
        System.out.println("Child static block");
    }

    {
        System.out.println("Child instance block");
    }

    public Child() {
        System.out.println("Child constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        new Child(); // 创建Child类的实例
    }
}

这段代码的输出将会按以下顺序:

scss 复制代码
Parent static block
Child static block
Parent instance block
Parent constructor
Child instance block
Child constructor

静态内容(静态变量和静态初始化块)在类首次被加载到JVM时初始化。对于实例内容(实例变量、实例初始化块、构造函数),每次创建类的对象实例时都会初始化。优先初始化父类内容,然后是子类内容。

以下是Java对象初始化的顺序的图示概览。这个顺序适用于当一个类被实例化时JVM所执行的步骤:

diff 复制代码
+-------------------------------------------------------+
|         类加载(如果该类未被加载过)                  |
+-------------------------------------------------------+
| 1. 父类静态变量和静态初始化块(按声明顺序)            |
| 2. 子类静态变量和静态初始化块(按声明顺序)            |
+-------------------------------------------------------+
|                 创建对象实例                           |
+-------------------------------------------------------+
| 3. 父类实例变量声明和实例初始化块(按声明顺序)        |
| 4. 父类构造函数                                       |
|                                                       |
| 5. 子类实例变量声明和实例初始化块(按声明顺序)        |
| 6. 子类构造函数                                       |
+-------------------------------------------------------+

这个图表说明了在Java中,一个类从被加载到实例化的过程中不同初始化动作的顺序。注意,如果已经加载了父类,那么在子类被加载时,不会再次加载父类。类加载时,静态初始化只会执行一次。实例初始化每次创建对象时都会执行。

这个顺序保证了在父类构造函数执行之前,父类的实例变量已经被初始化,同样地,在子类构造函数执行之前,子类的实例变量已经被初始化。这是语言设计上确保对象正确构建的重要规则。

在Java中,静态初始化块(Static Initialization Block)和实例初始化块(Instance Initialization Block)是两种用于初始化类和对象状态的代码块。

静态初始化块

静态初始化块是一个在类中定义的,用大括号{}包围的代码块,并且它前面有static关键字。它用于初始化类的静态变量。在类被加载到JVM时,静态初始化块被执行,并且无论创建了多少对象实例,它只执行一次。

例如:

csharp 复制代码
class MyClass {
    static int staticVariable;
    
    static {
        staticVariable = 10;
        System.out.println("Static initialization block called.");
    }
}

实例初始化块

实例初始化块是一个在类中定义的,用大括号{}包围的代码块,没有static关键字。它在构造对象时执行,每次创建类的一个新实例时,实例初始化块都会执行,并且它在构造函数之前执行。实例初始化块可以用来初始化那些不能在声明时初始化的复杂实例变量。

例如:

csharp 复制代码
class MyClass {
    int instanceVariable;
    
    {
        instanceVariable = 20;
        System.out.println("Instance initialization block called.");
    }
    
    MyClass() {
        System.out.println("Constructor called.");
    }
}

在上面的例子中,每次使用new MyClass()创建对象时,实例初始化块先于构造函数执行。

使用场景

  • 静态初始化块通常用于初始化复杂的静态变量,或者在类首次加载时执行某些只需要进行一次的操作。
  • 实例初始化块用于在调用任何构造函数之前执行一些操作或初始化块。如果类包含多个构造函数,并且这些构造函数中有重复的初始化代码,可以将这些代码放入实例初始化块中以避免重复。

值得注意的是,实例初始化块并不是Java编程中的常用做法,因为多数情况下,初始化逻辑可以直接放到构造函数中。然而,静态初始化块由于其特殊的定位和初始化时机,在初始化复杂静态数据时是必要的。

相关推荐
在努力的前端小白2 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
一叶飘零_sweeeet4 小时前
从繁琐到优雅:Java Lambda 表达式全解析与实战指南
java·lambda·java8
艾伦~耶格尔5 小时前
【集合框架LinkedList底层添加元素机制】
java·开发语言·学习·面试
一只叫煤球的猫5 小时前
🕰 一个案例带你彻底搞懂延迟双删
java·后端·面试
最初的↘那颗心5 小时前
Flink Stream API 源码走读 - print()
java·大数据·hadoop·flink·实时计算
JH30736 小时前
Maven的三种项目打包方式——pom,jar,war的区别
java·maven·jar
带刺的坐椅7 小时前
轻量级流程编排框架,Solon Flow v3.5.0 发布
java·solon·workflow·flow·solon-flow
David爱编程7 小时前
线程调度策略详解:时间片轮转 vs 优先级机制,面试常考!
java·后端
阿冲Runner8 小时前
创建一个生产可用的线程池
java·后端
写bug写bug8 小时前
你真的会用枚举吗
java·后端·设计模式