在 Java 中,单例模式(Singleton)的常见实现方式有以下几种,每种方式都有其特点和适用场景:
1. 饿汉式(Eager Initialization)
在类加载时直接初始化实例,线程安全但可能浪费资源(如果实例未被使用)。
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {} // 私有构造器
public static EagerSingleton getInstance() {
return INSTANCE;
}
}优点:实现简单,线程安全。
缺点:实例在类加载时创建,可能占用不必要的资源。
2. 懒汉式(Lazy Initialization,非线程安全)
延迟实例化,但非线程安全,多线程环境下可能创建多个实例。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}优点:延迟加载,节省资源。
缺点:非线程安全。
3. 懒汉式(线程安全版,synchronized方法)
通过 `synchronized` 方法保证线程安全,但性能较差。
public class SynchronizedSingleton {
private static SynchronizedSingleton instance;
private SynchronizedSingleton() {}
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}优点:线程安全,延迟加载。
缺点:每次获取实例都需同步,性能开销大。
4. 双重检查锁(Double-Checked Locking)
结合 `synchronized` 块和 `volatile` 关键字,兼顾线程安全和性能。
public class DCLSingleton {
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (DCLSingleton.class) {
if (instance == null) { // 第二次检查
instance = new DCLSingleton();
}
}
}
return instance;
}
}优点:线程安全,延迟加载,性能较好。
缺点:实现稍复杂,需注意 `volatile` 的使用。
5. 静态内部类(Static Inner Class)
利用类加载机制保证线程安全,延迟加载且无同步开销。
public class InnerClassSingleton {
private InnerClassSingleton() {}
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}优点:线程安全,延迟加载,无需同步。
缺点:无法通过参数初始化实例。
6. 枚举(Enum Singleton)
通过枚举实现,天然防止反射攻击和序列化问题,是《Effective Java》推荐的方式。
public enum EnumSingleton {
INSTANCE; // 单例实例
// 可以添加其他成员变量和方法
private String value;
// 私有构造器(枚举的构造器默认是私有的,可以省略 private)
EnumSingleton() {
value = "Initial Value";
}
// 获取单例实例
public static EnumSingleton getInstance() {
return INSTANCE;
}
// 业务方法
public void doSomething() {
System.out.println("Doing something with value: " + value);
}
// 设置值
public void setValue(String value) {
this.value = value;
}
// 获取值
public String getValue() {
return value;
}
}使用枚举单例
public class Main {
public static void main(String[] args) {
// 获取单例实例
EnumSingleton singleton = EnumSingleton.getInstance();
// 调用业务方法
singleton.doSomething();
// 修改值
singleton.setValue("New Value");
// 再次调用业务方法
singleton.doSomething();
// 验证单例
EnumSingleton anotherInstance = EnumSingleton.getInstance();
System.out.println("Is singleton the same instance? " + (singleton == anotherInstance));
}
}枚举单例的优点
线程安全:枚举实例的创建由 JVM 保证,天然线程安全。
防止反射攻击:枚举类型不能通过反射创建实例,避免了反射破坏单例。
防止序列化破坏:枚举单例在序列化和反序列化时不会创建新的实例。
简洁易读:代码简洁,无需额外处理线程安全、反射和序列化问题。
枚举单例的缺点
无法继承:枚举类型不能继承其他类(但可以实现接口)。
不够灵活:枚举单例的实例在类加载时创建,无法延迟加载
7. 注意事项
- 反射攻击:除枚举外,其他方式需在构造器中防止重复创建(抛出异常)。
- 序列化:非枚举单例需实现 `readResolve()` 方法防止反序列化生成新实例。

0条评论
点击登录参与评论