代码块与初始化顺序
代码块与初始化顺序
Java 常见代码块包括局部代码块、静态初始化块、实例初始化块和构造器。
局部代码块
方法内部的花括号创建局部作用域,可以缩短变量生命周期。
代码块收起展开
void process() {
{
int temporary = 10;
System.out.println(temporary);
}
// temporary 在这里不可见
}静态初始化块
代码块收起展开
class Config {
static final Map<String, String> DEFAULTS;
static {
Map<String, String> values = new HashMap<>();
values.put("mode", "prod");
DEFAULTS = Map.copyOf(values);
}
}- 类初始化时执行,通常每个类加载器只执行一次。
- 只能直接访问静态成员,不能使用 this。
- 静态字段初始化和静态块按源码顺序执行。
- 类已加载不等于类已初始化,首次主动使用才通常触发初始化。
实例初始化块
代码块收起展开
class User {
private final List<String> roles;
{
roles = new ArrayList<>();
roles.add("USER");
}
User() {
System.out.println("constructor");
}
}每次创建对象都会执行。编译器会把逻辑合并进构造器,适合共享初始化;实际项目通常优先字段初始化、构造器委托或私有方法,因为更直观。
单个类的执行顺序
首次 new 一个类时:
- 静态字段与静态块按源码顺序执行,仅一次。
- 实例字段与实例块按源码顺序执行,每个对象一次。
- 构造器体。
再次创建对象只执行第 2、3 步。
继承场景
- 父类静态初始化。
- 子类静态初始化。
- 父类实例字段和实例块。
- 父类构造器。
- 子类实例字段和实例块。
- 子类构造器。
构造器第一条语句必须是 this(…) 或 super(…);未写时编译器插入无参 super()。
常见陷阱
- 不要在构造器中调用可被子类重写的方法。
- 静态初始化失败会抛出 ExceptionInInitializerError。
- 静态块不适合慢 I/O 或外部环境依赖。
- 字段互相引用时要留意源码顺序和默认值。