| 方法 | 适用场景 | 是否真正“在构造函数之前” |
|---|---|---|
| 静态代码块 | 类加载时初始化 | ✅ 是(比构造函数更早) |
| 工厂方法 | 每次实例化前执行逻辑 | ✅ 是(逻辑在构造函数外部) |
| AOP/代理 | 对多个类统一处理 | ✅ 是(通过字节码增强) |
| 构造函数内部逻辑 | 初始化对象前做一些准备 | ❌ 否(逻辑在构造函数内) |
在 Java 对象实例化过程中,构造方法是创建对象的必经之路,但确实有几种技术可以在构造方法执行之前执行逻辑。以下是几种实现方式:
1. 静态初始化块(类级别)
在类加载时执行,早于任何实例的创建(包括构造方法):
public class MyBean {
// 静态初始化块 - 在类加载时执行
static {
System.out.println("静态初始化块执行(构造方法之前)");
}
public MyBean() {
System.out.println("构造方法执行");
}
}
特点:
- 只执行一次(类加载时)
- 无法访问实例成员
- 适用于全局初始化(如加载静态资源)
2. 实例初始化块(对象级别)
在每次创建对象时,在构造方法之前执行:
public class MyBean {
// 实例初始化块 - 每次实例化时在构造方法前执行
{
System.out.println("实例初始化块执行(构造方法之前)");
}
public MyBean() {
System.out.println("构造方法执行");
}
}
执行顺序:
- 静态初始化块(首次加载类时)
- 实例初始化块(每次实例化时)
- 构造方法
3. 使用工厂模式(最灵活)
通过工厂方法控制实例化过程:
public class MyBean {
private MyBean() {
System.out.println("构造方法执行");
}
public static MyBean createInstance() {
System.out.println("前置逻辑执行(构造方法之前)");
return new MyBean();
}
}
// 使用方式
MyBean bean = MyBean.createInstance();
4. 使用依赖注入框架(如Spring)
通过@Bean工厂方法实现:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// 构造方法前的逻辑
System.out.println("执行前置逻辑");
return new MyBean(); // 实际构造
}
}
5. 字节码增强技术(高级用法)
使用字节码操作库(如 ASM、Byte Buddy)在编译期/加载期修改字节码:
new ByteBuddy()
.subclass(MyBean.class)
.method(named("<init>"))
.intercept(SuperMethodCall.INSTANCE.andThen(
MethodCall.invoke(MyBean.class.getMethod("postConstructor"))
)
.make();
关键区别总结
| 方式 | 执行时机 | 执行次数 | 能否访问实例成员 |
|---|---|---|---|
| 静态初始化块 | 类加载时 | 一次 | 否 |
| 实例初始化块 | 每次实例化时 | 多次 | 是(部分初始化) |
| 工厂方法 | 实例化前 | 多次 | 是(完全控制) |
Spring @Bean 工厂 | 容器初始化时 | 一次 | 是 |
实际应用建议
- 常规需求:使用实例初始化块(简单直接)
- 复杂初始化:使用工厂模式(完全控制流程)
- 框架集成:使用 Spring 的
@Bean工厂方法 - 全局初始化:静态初始化块 + 静态方法
重要提示:在构造方法之前执行的逻辑中,无法访问尚未初始化的实例成员(因为对象尚未完全构造)。此时访问的成员变量可能是默认值(0、null等),而非构造方法中设置的值。