防范构造函数中 this 逃逸的关键是禁止在对象完全初始化前发布 this,包括注册监听、启动线程、存入静态容器等;所有关键字段须为 final 并在构造参数中直接赋值;高风险操作应移至显式 init() 方法;异常应精准反映对象不可创建语义。

防范构造函数中过早抛出异常导致未完全初始化对象逃逸,关键不是“压住异常”,而是切断 this 在对象尚不安全时向外传播的路径。异常本身不可怕,可怕的是它发生前已把半成品对象暴露出去。
确保异常抛出前 never 发布 this
只要 this 被传给外部(注册监听、启动线程、存入静态容器、作为参数传递),哪怕只有一行代码,就构成逃逸。异常抛出无法撤销这一事实。
- 禁止在构造函数内调用任何可能捕获 this 的方法,包括匿名内部类、lambda 表达式、方法引用(如
source.register(this::onEvent)) - 检查所有被调用的私有方法——它们是否间接触发了对外发布?例如
initListeners()内部调用了eventBus.subscribe(this) - 第三方库集成点要特别小心:Spring 的
@EventListener、RxJava 的subscribe()、日志框架的异步 Appender 等,都可能在构造期间悄悄持有 this
用 final 字段 + 安全赋值堵住重排序漏洞
即使没显式发布 this,JVM 可能将字段写操作重排序到构造函数末尾之后。其他线程仍可能看到部分初始化状态。
- 所有关键状态字段声明为 final,并在构造函数参数列表中直接赋值(而非先设默认值再修改)
- 避免在构造函数中对非 final 字段做多次写入,尤其不要在赋值后又调用可能被子类重写的方法
- final 字段一旦赋值,JVM 保证其对其他线程可见,这是唯一能依赖的安全发布机制
把高风险动作移出构造函数
资源加载、网络连接、监听注册等操作,天然带有失败可能性和副作用,不应绑定在构造语义里。
- 提供显式的
init()或start()方法,由调用方在确认对象创建成功后再触发 - 使用静态工厂方法封装完整流程:先 new,再 init,失败则丢弃对象,绝不返回半成品
- 对必须延迟绑定的组件(如回调监听器),改用弱引用或生命周期感知设计(如
WeakReference<Listener>或 Android 的LifecycleObserver)
异常类型与处理方式需匹配失败语义
构造函数抛异常不是为了“报错”,而是向调用方声明:“这个对象根本不能存在”。因此异常必须精准、可控、可恢复。
- 参数校验失败用
IllegalArgumentException,空指针用NullPointerException,业务规则违例用自定义IllegalStateException子类 - 避免在构造函数中抛受检异常(如
IOException),它会破坏依赖注入、Builder 模式和反序列化;改用静态工厂方法包装为运行时异常 - 若确实需要资源级失败语义(如配置加载失败),应让工厂方法返回
Optional<T>或Result<T, Error>,而不是让构造函数承担失败通知职责

















