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

相关推荐
青山师9 小时前
动态代理深度解析:JDK与CGLIB底层实现与实战
java·设计模式·面试·动态代理·java面试·cglib
放学后的泡泡9 小时前
提供一个工作流的表设计
java·设计规范
生活真难10 小时前
SpringCloud - 任务调度 - xxl-job-java
java·spring boot·spring cloud
人道领域10 小时前
【黑马点评日记】:用户签到功能详解——从Bitmap入门到避坑指南
java·数据库·redis·后端
梦梦代码精10 小时前
《企业开源商城选型:商业闭环、二次开发与成本平衡》
java·开发语言·低代码·开源·github
狼与自由11 小时前
灰度发布的策略
java
神仙别闹11 小时前
基于QT(C++)实现线性表的建立、插入、删除、查找等基本操作
java·c++·qt
Kiyra11 小时前
从上传到可问答:Interview Agent 的知识库 RAG 链路
java·人工智能·后端·spring·职场和发展
拙野11 小时前
工作中Mybatis动态SQL的使用
java·sql·mybatis
AI人工智能+电脑小能手11 小时前
【大白话说Java面试题】【Java基础篇】第39题:说说反射的用途及实现原理,Java获取反射(Class)的三种方法
java·开发语言·后端·python·面试