【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编程中的常用做法,因为多数情况下,初始化逻辑可以直接放到构造函数中。然而,静态初始化块由于其特殊的定位和初始化时机,在初始化复杂静态数据时是必要的。

相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫3 小时前
泛型(2)
java
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶5 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb