封装与单例天然协作:私有构造器是基石,静态工厂方法封装实例获取逻辑,instance字段须private static final;需防御反射、序列化和克隆破坏。

封装和单例模式在 Java 中是天然协作的关系:封装隐藏了对象的创建细节,单例则借助封装确保全局唯一实例。核心在于——把构造器设为私有,用静态方法控制访问入口,再配合访问修饰符和类结构设计,从语法和逻辑双重层面限制对象数量。
私有构造器是封装与单例的共同基石
单例依赖封装来阻止外部 new 实例,关键就是把构造器声明为 private。这样即使类被继承或反射调用(未额外防护时),普通代码也无法绕过封装直接构造对象。
- 不写构造器时,Java 提供默认 public 构造器,必须显式定义 private 构造器覆盖它
- 若类中已有其他构造器(如带参构造),默认构造器不会自动生成,但仍需手动添加 private 无参构造器以彻底封禁实例化
- 构造器私有后,new A() 在类外编译直接报错,这是编译期最硬的控制手段
静态工厂方法封装实例获取逻辑
通过 public static 方法(如 getInstance())对外提供实例,这个方法内部完成判断、创建、返回等全部逻辑,外部只看到“取对象”,看不到“怎么造”。这就是典型的封装:隐藏实现,暴露契约。
- 懒汉式中,getInstance() 检查 instance 是否为 null,是则 new 并赋值;否则直接返回。整个过程对外不可见
- 饿汉式中,instance 在类加载时就初始化,getInstance() 只做简单返回,但依然用 static 方法统一出口,保持接口一致
- 可在此方法中加入线程安全控制(如 synchronized 或双重检查锁)、日志、监控等增强逻辑,不影响调用方代码
实例字段必须私有且 final(推荐)
单例的唯一实例变量(如 private static Singleton instance)本身也要封装:用 private 修饰防止外部篡改,加 static 表明属于类,加 final 保证引用不可变(避免被重新赋值破坏单例性)。
立即学习“Java免费学习笔记(深入)”;
- 不加 final 时,若 getInstance() 方法逻辑有误或被恶意调用,instance 可能被重复 new 覆盖
- final + private + static 组合,让 instance 成为真正受控的、不可更改的类级常量
- 即使使用枚举单例(public enum Singleton { INSTANCE; }),其本质也是 JVM 封装了构造与实例管理,比手写更安全
避免反模式:别让封装失效
封装不是写了 private 就万事大吉。以下情况会绕过单例约束,需主动防御:
- 反射攻击:通过 Constructor.setAccessible(true) 强行调用私有构造器。可在构造器内加标志位校验,首次调用后抛异常
- 序列化/反序列化:若类实现 Serializable,反序列化会生成新对象。应实现 readResolve() 方法,强制返回原有 instance
- 克隆破坏:若类实现 Cloneable,重写 clone() 方法并抛 CloneNotSupportedException,或在 clone() 中返回 this


















