空对象模式是用行为明确的nulluser等替代null以避免重复判空;适用于logger等依赖且空行为确定的场景,不适用于user等需显式处理的核心实体。

空对象模式不是让 null 变得“安全”,而是用一个真正存在的、行为明确的 NullUser 或 NullLogger 替掉 null,从而避免满屏 if (obj != null)。
什么时候该用空对象,而不是直接判空?
当你反复在多个地方对同一个类型做非空检查,且它的“空行为”是确定的(比如:不打印日志、返回空集合、不做任何处理),就适合抽成空对象。
- 常见场景:
Logger、Service、Strategy、Observer这类被注入或查找出来的依赖 - 不适合场景:
User、Order这类核心业务实体——它们的null往往代表“数据缺失”,需要显式处理,不能静默忽略 - 注意:空对象必须实现和真实对象相同的接口,否则调用方无法无感切换
怎么写一个靠谱的空对象?
关键不是“什么都不做”,而是“做一件明确、可预期、不抛异常的事”。比如 NullLogger 的 info() 方法应该记录一条 trace 日志,或者干脆什么也不做,但绝不能抛 UnsupportedOperationException。
- 构造函数应为
private或protected,通过静态常量暴露唯一实例:NullLogger.INSTANCE - 所有方法体必须有明确实现,禁止留空(
{})或 thrownew UnsupportedOperationException() - 如果接口返回集合,空对象应返回
Collections.emptyList()而不是null;返回基本类型就用默认值(0、false) - 避免在空对象里加日志或埋点——它本意是“静默降级”,加日志反而污染调用方行为
为什么用了空对象还报 NullPointerException?
最常见原因是:你只替换了部分路径,没覆盖全部创建点。空对象只有在“所有可能产生 null 的地方都被替换成它”时才真正生效。
立即学习“Java免费学习笔记(深入)”;
- 工厂方法没统一:有的地方 new
RealService(),有的地方 newNullService(),但漏了某个配置分支 - Spring 中没配好
@Primary或 profile 条件,导致测试环境用了NullService,生产环境却因配置加载顺序问题仍拿到null - 空对象本身被设为
null:比如static NullLogger instance = null;且忘了初始化 - 误把空对象当工具类用:比如写了
NullUser.getName()返回"",但调用方接着对这个空字符串做.split(",")[0]——这不是空对象的问题,是业务逻辑没兜住边界
空对象模式真正的难点不在写法,而在于团队对“什么算合理空行为”的共识。同一个 NullPaymentService,有人觉得该返回成功,有人觉得该返回失败,这种分歧比代码更难调试。










