Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

  • JVM

    • JVM概述
    • JVM基础

    • 类加载机制

      • 类的生命周期
      • 类加载器
      • 双亲委派模型
      • 类初始化顺序
        • 1. 类初始化触发时机
        • 2. 静态初始化顺序
        • 3. 父子类初始化顺序
        • 4. 示例
        • 5. 初始化陷阱
        • 6. 单例模式与初始化
        • 7. 初始化顺序图
        • 8. 字段顺序陷阱
        • 9. 初始化和多态陷阱
        • 10. 开发建议
        • 专家速查表
        • 11. Tips 快问快答
        • 12. 总结
      • 类加载问题与SPI
    • 运行时内存

    • 垃圾回收

    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

类初始化顺序

类初始化顺序是 Java 基础和 JVM 之间的交叉点。很多静态变量为 null、父子类初始化异常、单例模式问题,都和初始化顺序有关。

# 1. 类初始化触发时机

类首次主动使用时会触发初始化。

常见主动使用包括:

  • 创建类实例。
  • 访问类的静态变量。
  • 调用类的静态方法。
  • 使用反射调用类。
  • 初始化子类时先初始化父类。
  • JVM 启动时初始化主类。

访问编译期常量通常不会触发类初始化。

public static final int VALUE = 100;

# 2. 静态初始化顺序

类内部静态变量和静态代码块按源码顺序执行。

public class Demo {
    static int a = 1;

    static {
        a = 2;
    }

    static int b = a;
}

最终 a = 2,b = 2。

如果顺序变化,结果可能不同。

# 3. 父子类初始化顺序

创建子类对象时,顺序大致是:

  1. 父类静态变量和静态代码块。
  2. 子类静态变量和静态代码块。
  3. 父类实例变量和实例代码块。
  4. 父类构造方法。
  5. 子类实例变量和实例代码块。
  6. 子类构造方法。

静态初始化只执行一次,实例初始化每次创建对象都会执行。

# 4. 示例

class Parent {
    static {
        System.out.println("parent static");
    }

    {
        System.out.println("parent block");
    }

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

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

    {
        System.out.println("child block");
    }

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

执行:

new Child();

输出:

parent static
child static
parent block
parent constructor
child block
child constructor

# 5. 初始化陷阱

不要在父类构造方法中调用可被子类重写的方法。

class Parent {
    Parent() {
        init();
    }

    void init() {
    }
}

class Child extends Parent {
    private String name = "child";

    @Override
    void init() {
        System.out.println(name);
    }
}

父类构造方法执行时,子类字段可能还没有完成初始化,容易出现意外结果。

# 6. 单例模式与初始化

静态内部类单例利用类初始化的线程安全性:

public class Singleton {
    private Singleton() {
    }

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

Holder 只有在调用 getInstance() 时才初始化,JVM 保证类初始化过程线程安全。

# 7. 初始化顺序图

创建子类对象 new Child()
 │
 ├─ 父类是否已初始化?
 │   └─ 否 -> 初始化父类静态字段和静态代码块
 │
 ├─ 子类是否已初始化?
 │   └─ 否 -> 初始化子类静态字段和静态代码块
 │
 ├─ 分配对象内存并设置零值
 │
 ├─ 执行父类实例字段和实例代码块
 │
 ├─ 执行父类构造方法
 │
 ├─ 执行子类实例字段和实例代码块
 │
 └─ 执行子类构造方法

静态部分属于类,只执行一次;实例部分属于对象,每次 new 都执行。

# 8. 字段顺序陷阱

同一个类内部,静态字段和静态代码块按源码顺序执行:

public class Demo {
    static int a = b + 1;
    static int b = 10;
}

结果:

a = 1
b = 10

因为执行 a = b + 1 时,b 还只是准备阶段默认值 0。

# 9. 初始化和多态陷阱

父类构造方法中调用可重写方法是危险的:

class Parent {
    Parent() {
        init();
    }

    void init() {
    }
}

class Child extends Parent {
    private String name = "child";

    @Override
    void init() {
        System.out.println(name.length());
    }
}

父类构造方法执行时,子类字段可能还未初始化,name 可能是 null,导致 NPE。

# 10. 开发建议

  • 静态字段初始化不要互相依赖复杂顺序。
  • 静态代码块不要执行网络、数据库等外部操作。
  • 构造方法中不要调用可被重写的方法。
  • 单例推荐枚举或静态内部类。
  • 静态初始化失败要有明确日志。

# 专家速查表

初始化元素 执行时机 顺序规则
静态字段赋值 类初始化阶段 按源码顺序
静态代码块 类初始化阶段 和静态字段按出现顺序交织
实例字段赋值 对象创建阶段 super 构造后、本类构造前
实例代码块 对象创建阶段 和实例字段按出现顺序交织
构造方法 对象初始化末尾 先父类后子类

类初始化问题最常见于静态字段互相依赖、父子类构造中调用可重写方法、单例初始化顺序错误。排查时按“加载类 -> 初始化静态成员 -> 创建对象 -> 初始化实例成员 -> 构造方法”拆开看。

# 11. Tips 快问快答

Q:静态代码块一定先于构造方法执行吗?

A:是。同一个类首次初始化时,静态变量和静态代码块先执行;创建对象时再执行实例初始化和构造方法。

Q:父类静态代码块和子类静态代码块谁先执行?

A:父类先执行,子类后执行。

Q:访问 static final 常量会触发初始化吗?

A:如果是编译期常量,通常不会触发定义类初始化。

Q:类初始化是否线程安全?

A:是。JVM 保证类初始化过程同步,同一个类不会被多个线程并发执行初始化逻辑。

# 12. 总结

类初始化顺序的核心规则是:父类先于子类,静态先于实例,同级按源码顺序。理解这些规则,可以避免很多隐藏在静态变量、继承和构造方法里的问题。

上次更新: 2026/06/25, 14:19:18
双亲委派模型
类加载问题与SPI

← 双亲委派模型 类加载问题与SPI→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式