2025-02-17 11:32

java中的单一实例写法有哪几种

王姐姐

Java后端

(119)

(0)

收藏

在 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));
    }
}

枚举单例的优点

  1. 线程安全:枚举实例的创建由 JVM 保证,天然线程安全。

  2. 防止反射攻击:枚举类型不能通过反射创建实例,避免了反射破坏单例。

  3. 防止序列化破坏:枚举单例在序列化和反序列化时不会创建新的实例。

  4. 简洁易读:代码简洁,无需额外处理线程安全、反射和序列化问题。

枚举单例的缺点

  1. 无法继承:枚举类型不能继承其他类(但可以实现接口)。

  2. 不够灵活:枚举单例的实例在类加载时创建,无法延迟加载

7. 注意事项

- 反射攻击:除枚举外,其他方式需在构造器中防止重复创建(抛出异常)。

- 序列化:非枚举单例需实现 `readResolve()` 方法防止反序列化生成新实例。

0条评论

点击登录参与评论