80. Java 枚举类 - 使用枚举实现单例模式

文章目录

  • [80. Java 枚举类 - 使用枚举实现单例模式](#80. Java 枚举类 - 使用枚举实现单例模式)
    • [**1️⃣ 为什么用枚举实现单例?**](#1️⃣ 为什么用枚举实现单例?)
    • [**2️⃣ 枚举实现单例模式**](#2️⃣ 枚举实现单例模式)
    • [**3️⃣ 枚举单例如何防止反射攻击?**](#3️⃣ 枚举单例如何防止反射攻击?)
    • [**4️⃣ 枚举单例如何防止反序列化破坏?**](#4️⃣ 枚举单例如何防止反序列化破坏?)
    • [**5️⃣ 枚举单例 vs 传统单例**](#5️⃣ 枚举单例 vs 传统单例)
    • [**6️⃣ 枚举单例的适用场景**](#6️⃣ 枚举单例的适用场景)
    • [**7️⃣ 扩展:枚举单例 + 接口**](#7️⃣ 扩展:枚举单例 + 接口)
    • [**🔹 总结**](#🔹 总结)
    • [**8️⃣ 总结**](#8️⃣ 总结)
      • [**🎯 为什么推荐用枚举实现单例?**](#🎯 为什么推荐用枚举实现单例?)
      • [**🎯 什么时候不要用枚举单例?**](#🎯 什么时候不要用枚举单例?)

80. Java 枚举类 - 使用枚举实现单例模式

在 Java 中,单例模式(Singleton Pattern) 是一种常见的设计模式,保证一个类只有一个实例,并提供全局访问点。

使用枚举(Enum)实现单例最简单、最安全的方式! 🚀


1️⃣ 为什么用枚举实现单例?

传统的单例模式

  • 可能会被反射破坏 (通过 setAccessible(true) 访问私有构造方法)。
  • 可能会被反序列化破坏(对象在反序列化时被重新创建)。
  • 需要额外的代码保证线程安全

枚举单例的优势

防止反射攻击 (枚举构造方法是 private 且不能通过反射创建新实例)。

防止反序列化破坏 (枚举单例在反序列化时不会创建新实例)。

天然线程安全JVM 保证 enum 的实例创建是线程安全的)。

代码更简洁(比传统单例模式少很多代码)。


2️⃣ 枚举实现单例模式

java 复制代码
public enum SomeSingleton {
    INSTANCE;  // 唯一实例

    // 可以添加其他成员变量和方法
    private int value;

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void doSomething() {
        System.out.println("Doing something...");
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        // 获取单例实例
        SomeSingleton singleton1 = SomeSingleton.INSTANCE;
        SomeSingleton singleton2 = SomeSingleton.INSTANCE;

        // 设置值
        singleton1.setValue(42);

        // 两个引用指向同一个对象
        System.out.println(singleton1.getValue()); // 输出 42
        System.out.println(singleton2.getValue()); // 也是 42

        // 调用方法
        singleton1.doSomething();
    }
}

💡 输出

java 复制代码
42
42
Doing something...

📌 代码解析

  • INSTANCE唯一的实例不会创建新的对象
  • setValue()getValue() 让我们可以存储数据(类似单例对象中的成员变量)。
  • doSomething() 是一个普通方法,可以调用它做一些业务逻辑。

3️⃣ 枚举单例如何防止反射攻击?

在普通单例模式中,可以使用反射绕过私有构造方法

java 复制代码
Constructor<SomeSingleton> constructor = SomeSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
SomeSingleton newInstance = constructor.newInstance();  // ❌ 破坏单例!

💥 错误

java 复制代码
java.lang.NoSuchMethodException: SomeSingleton.<init>()

📌 枚举不会暴露构造方法 ,所以反射无法创建新实例


4️⃣ 枚举单例如何防止反序列化破坏?

在普通单例模式中,反序列化可能会创建新实例

java 复制代码
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(singleton1);
out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
SomeSingleton deserializedInstance = (SomeSingleton) in.readObject();

💡 如果是普通单例,会创建新对象! 但如果使用枚举,反序列化不会创建新实例

java 复制代码
SomeSingleton singleton1 = SomeSingleton.INSTANCE;
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
out.writeObject(singleton1);
out.close();

ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));
SomeSingleton singleton2 = (SomeSingleton) in.readObject();

System.out.println(singleton1 == singleton2);  // ✅ 永远返回 true

📌 枚举的 readResolve() 方法会保证反序列化后还是同一个实例


5️⃣ 枚举单例 vs 传统单例

方式 是否线程安全 反射攻击 反序列化安全 代码复杂度
枚举单例 ✅ 安全 ✅ 防止 ✅ 防止 最简洁
懒汉式(synchronized) ✅ 安全 ❌ 有风险 ❌ 有风险 ❌ 代码复杂
双重检查锁(DCL) ✅ 安全 ❌ 有风险 ❌ 有风险 ❌ 代码复杂
静态内部类 ✅ 安全 ❌ 有风险 ❌ 有风险 ✅ 代码较简洁

💡 结论

  • 推荐使用枚举单例 ,因为它最安全,代码最少
  • 如果单例需要懒加载(Lazy Initialization) ,可以考虑 静态内部类

6️⃣ 枚举单例的适用场景

适合使用枚举单例的情况

  • 全局唯一对象(比如:日志管理器、配置管理、数据库连接池)。
  • 需要防止反射和反序列化破坏的单例
  • 不需要懒加载(枚举单例是饿汉式的,类加载时就创建)。

不适合使用枚举单例的情况

  • 如果需要懒加载 ,枚举 不支持延迟初始化 (可以用 静态内部类)。
  • 如果需要继承其他类 ,枚举不能继承其他类(但可以实现接口)。

7️⃣ 扩展:枚举单例 + 接口

如果单例需要实现某些接口 ,枚举是可以实现的

java 复制代码
interface Service {
    void execute();
}

public enum SingletonService implements Service {
    INSTANCE;

    @Override
    public void execute() {
        System.out.println("Executing service...");
    }
}

public class EnumInterfaceTest {
    public static void main(String[] args) {
        SingletonService.INSTANCE.execute();
    }
}

💡 输出

java 复制代码
Executing service...

📌 解析

  • SingletonService 实现了 Service 接口 ,并提供了 execute() 方法。
  • INSTANCE.execute() 可以直接调用接口方法

🔹 总结

特点 枚举单例
是否最安全 (防反射 & 反序列化攻击)
是否线程安全 (JVM 保证)
是否最简洁 (不用写 synchronizedvolatile
是否支持懒加载 (枚举是饿汉式单例)
是否能继承类 不能(枚举不能继承类,但可以实现接口)

💡 推荐使用枚举单例,除非需要懒加载! 🚀


8️⃣ 总结

🎯 为什么推荐用枚举实现单例?

代码最简单 ,不用写 synchronizedvolatile

线程安全 ,JVM 保证 enum 只会创建一个实例

防止反射攻击 ,枚举没有公开构造方法

防止反序列化攻击 ,不会创建新实例

可以实现接口,用于全局管理对象


🎯 什么时候不要用枚举单例?

需要懒加载 (用静态内部类更合适)

需要继承其他类(枚举不能继承类)


希望这个讲解能帮你彻底掌握 如何用枚举实现单例模式!🚀 🎯

相关推荐
南部余额9 分钟前
Python OOP核心技巧:如何正确选择实例方法、类方法和静态方法
开发语言·python
supingemail15 分钟前
面试之 Java 新特性 一览表
java·面试·职场和发展
星星点点洲21 分钟前
【Java】应对高并发的思路
java
LDM>W<24 分钟前
黑马点评-用户登录
java·redis
保利九里30 分钟前
数据类型转换
java·开发语言
Uranus^33 分钟前
使用Spring Boot与Spring Security构建安全的RESTful API
java·spring boot·spring security·jwt·restful api
蚂蚁在飞-34 分钟前
Golang基础知识—cond
开发语言·后端·golang
Aiden Targaryen34 分钟前
Windows/MacOS WebStorm/IDEA 中开发 Uni-App 配置
java·uni-app·webstorm
啾啾Fun44 分钟前
【Java微服务组件】分布式协调P1-数据共享中心简单设计与实现
java·分布式·微服务
Brilliant Nemo1 小时前
Vue2项目中使用videojs播放mp4视频
开发语言·前端·javascript