tulip notes
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Star-Lord

希望一天成为大师的学徒
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础与面向对象

    • Java中的一些概念
    • Java中的数组
    • String类跟关键字final
    • Java 日期时间与Date
    • Java中的异常处理
    • Java枚举
    • Java序列化和反序列化
    • Java中的注解
    • Java中的IO流
    • Java中的数据初始化
      • 初始化的背景
        • 一些追问
      • Java中数据的初始化-顺序
        • 静态变量和对象初始化
        • 静态数据初始化
        • 对象初始化
        • 总结
        • 为什么顺序是这样?(核心原因)
        • 加载顺序
        • 一些注意点
    • Java中抽象类与接口
  • 高级进阶

  • 并发合集

  • JVM合集

  • 实战细节与其他

  • 代码之丑与提升

  • 《Java》学习笔记
  • Java基础与面向对象
EffectTang
2025-11-09
目录

Java中的数据初始化

# Java中的数据初始化

# 初始化的背景

Java 程序在运行时,会涉及 类的加载过程(Class Loading)。

当类被使用时,JVM 会按顺序执行:

加载(Loading)
→ 验证(Verification)
→ 准备(Preparation)
→ 解析(Resolution)
→ 初始化(Initialization)
1
2
3
4
5

其中 初始化(Initialization) 是最后一步,它的作用是:

初始化阶段:执行类中的 初始化逻辑

  • 给 静态变量 赋值(包括显示赋值和静态代码块)
  • 执行类的 static {} 静态代码块
  • 确保类处于可用状态

它让类从“已加载”状态变成“已准备好正常使用”的状态。

# 一些追问

类初始化和构造方法是什么关系?

它们的关系是:类初始化是“类级别”的动作,构造方法是“对象级别”的动作。

类初始化 → 针对类||构造方法调用 → 针对对象实例

为什么要分“类初始化”和“构造方法”两个机制?

  1. 因为 Java 是面向对象的,类和对象是两个层级
  • 类本身需要被初始化(静态资源准备)
  • 对象实例也需要被初始化(实例字段准备)

如果不分层级,会造成混乱、效率低下。

# Java中数据的初始化-顺序

Java 的数据初始化流程,我们分为 两大部分:类初始化(静态) 和 对象初始化(实例)。

这里我们先不讨论,JVM那一层对文件的读取,加载这些步骤,只关注,Java中的类和对象以及对应数据(或者属性),他们的初始化顺序以及注意点是什么?

本次,我们只关注————初始化(Initialization),这个步骤。

# 静态变量和对象初始化

# 静态数据初始化

执行静态变量的显示初始化 + 静态代码块(按代码顺序),静态变量和静态代码块的初始化顺序严格按照代码中出现的顺序

  • 如果调用,那么顺序就是这样的,静态变量显式赋值 → 静态代码块 → 静态方法,但一定注意,先初始化的在前。
public class A {
    static int x = initX();   // 1
    static {                  // 2
        System.out.println("static block");
    }
    static int y = 100;       // 3
}
1
2
3
4
5
6
7

注意:

  • 初始化顺序问题:如果一个静态变量依赖于另一个静态变量,必须确保被依赖的变量先声明

  • 不能在局部变量中使用:static不能用于方法内部的局部变量

  • 不能访问非静态成员:静态变量不能直接访问实例变量或实例方法

  • 生命周期:与程序生命周期相同,从类加载开始到程序结束

# 对象初始化

接着是,对象初始化(Instance Initialization)

对应代码中就是,使用 new 创建对象时,如下列示例:

A a = new A();
1

每次创建对象时,实例的初始化,大概要经历以下几个过程:

  1. 父类的实例变量(默认初始化 → 显式初始化)
  2. 父类的实例代码块(即非静态代码块 )
  3. 父类的构造器
  4. 子类的实例变量(默认初始化 → 显式初始化)
  5. 子类的实例代码块
  6. 子类的构造器

下面是更详细的说明:

1.为对象分配内存,所有字段设为默认值

// 比如这样
int age = 18;
String name = "Tom";
1
2
3

2.调用父类构造器(super())

构造器第一行 隐式存在 super(),由 JVM 强制执行。

并且在调用构造方法前会执行父类的:

  • 实例字段初始化
  • 实例代码块
父类实例变量初始化
父类实例代码块
父类构造方法
1
2
3

3.初始化本类实例变量和实例代码块(按代码顺序)

比如像下面这样:

public class A {
    int x = initX();    // 1
    {                   // 2
        System.out.println("instance block");
    }
    int y = 10;         // 3

    A() {               // 4
        System.out.println("constructor");
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 总结

# 为什么顺序是这样?(核心原因)

Java 的初始化设计遵循 从父到子、从静态到实例、从默认值到显示赋值 的原则。

原因如下:

  • 静态属于类,本质上在对象存在前就必须完成

静态成员在 JVM 的 Method Area 中,但实例成员在堆里。 只有类先初始化,才能创建对象。

  • 父类决定子类的内存结构,因此必须先初始化父类

因为子类对象 包含父类部分(继承结构):

Object -> Parent -> Child
1
  • 构造方法要使用实例字段,因此字段必须先初始化

例如构造方法可能访问字段:

this.x += 1;
1

如果字段还未初始化,就无法正确执行构造逻辑。

# 加载顺序

类加载阶段(一次):
-------------------------------------------
1. 静态变量默认值
2. 静态变量显示初始化
3. 静态代码块
-------------------------------------------

对象实例化阶段(每次 new):
-------------------------------------------
4. 父类实例变量默认值
5. 父类实例变量显示初始化 + 父类实例代码块
6. 父类构造方法
-------------------------------------------
7. 子类实例变量默认值
8. 子类实例变量显示初始化 + 子类实例代码块
9. 子类构造方法
-------------------------------------------
对象创建完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 一些注意点

  • 变量和方法

“在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。”

  • 静态数据

“无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null。”

  • 代码块

代码块就是显式的初始化。非静态代码块,在创建对象时执行,且在构造方法之前执行。无论 new 多少个对象,静态代码块只运行一次,在类初始化时发生。

上次更新: 2025/11/11, 15:56:49
Java中的IO流
Java中抽象类与接口

← Java中的IO流 Java中抽象类与接口→

最近更新
01
Java中的垃圾回收
11-02
02
GitFlow的使用和注意
09-17
03
Spring中Bean的生命周期
09-03
更多文章>
Theme by Vdoing | Copyright © 2023-2025 EffectTang
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式