代码块与初始化顺序

代码块与初始化顺序

Java 常见代码块包括局部代码块、静态初始化块、实例初始化块和构造器。

局部代码块

方法内部的花括号创建局部作用域,可以缩短变量生命周期。

代码块JAVA · 7 行收起展开
void process() {
    {
        int temporary = 10;
        System.out.println(temporary);
    }
    // temporary 在这里不可见
}

静态初始化块

代码块JAVA · 9 行收起展开
class Config {
    static final Map<String, String> DEFAULTS;

    static {
        Map<String, String> values = new HashMap<>();
        values.put("mode", "prod");
        DEFAULTS = Map.copyOf(values);
    }
}
  • 类初始化时执行,通常每个类加载器只执行一次。
  • 只能直接访问静态成员,不能使用 this。
  • 静态字段初始化和静态块按源码顺序执行。
  • 类已加载不等于类已初始化,首次主动使用才通常触发初始化。

实例初始化块

代码块JAVA · 12 行收起展开
class User {
    private final List<String> roles;

    {
        roles = new ArrayList<>();
        roles.add("USER");
    }

    User() {
        System.out.println("constructor");
    }
}

每次创建对象都会执行。编译器会把逻辑合并进构造器,适合共享初始化;实际项目通常优先字段初始化、构造器委托或私有方法,因为更直观。

单个类的执行顺序

首次 new 一个类时:

  1. 静态字段与静态块按源码顺序执行,仅一次。
  2. 实例字段与实例块按源码顺序执行,每个对象一次。
  3. 构造器体。

再次创建对象只执行第 2、3 步。

继承场景

  1. 父类静态初始化。
  2. 子类静态初始化。
  3. 父类实例字段和实例块。
  4. 父类构造器。
  5. 子类实例字段和实例块。
  6. 子类构造器。

构造器第一条语句必须是 this(…) 或 super(…);未写时编译器插入无参 super()。

常见陷阱

  • 不要在构造器中调用可被子类重写的方法。
  • 静态初始化失败会抛出 ExceptionInInitializerError。
  • 静态块不适合慢 I/O 或外部环境依赖。
  • 字段互相引用时要留意源码顺序和默认值。

延伸阅读