枚举实现单例和策略模式并非炫技,而是利用其天然线程安全、不可反射破坏、自动序列化防护等特性;单例一行定义即成,策略通过各常量覆写抽象方法实现,二者还可融合为单例+策略一体化结构。

Java 中用枚举实现单例和策略模式,不是“炫技”,而是利用了枚举本身的语言特性和 JVM 保证——天然线程安全、不可反射破坏、自动序列化防护。它比手写双重检查锁或静态内部类更简洁可靠。
枚举实现单例模式:一行代码搞定
只需定义一个只含一个常量的枚举:
public enum ConfigManager {
INSTANCE;
<pre class="brush:php;toolbar:false;">private final Properties config = new Properties();
// 构造中可加载配置
ConfigManager() {
try {
config.load(getClass().getResourceAsStream("/config.properties"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String getProperty(String key) {
return config.getProperty(key);
}}
使用时直接调用:ConfigManager.INSTANCE.getProperty("db.url")
- INSTANCE 在类加载时由 JVM 唯一初始化,无需同步,天生线程安全
- 反序列化时 JVM 自动返回已有 INSTANCE,不会新建对象
- 无法通过反射调用构造器(枚举构造器隐式为 private 且被 JVM 特殊保护)
- 不支持延迟加载——如果初始化开销大,且可能全程不用,就不太适合
枚举实现策略模式:每个常量自带行为
把不同业务逻辑封装到各枚举实例中,避免 if-else 或 switch 分支蔓延:
public abstract double calculate(double price);
调用时按类型选择策略:DiscountStrategy.VIP_BONUS.calculate(1000.0)
- 每个枚举值可覆盖抽象方法,实现专属逻辑,扩展性强
- 编译期类型检查,传错策略会直接报错,不会等到运行时报 ClassCastException
- 配合工厂方法,还能统一入口:
DiscountStrategy.valueOf(type).calculate(price) - 比传统策略接口 + 多个实现类更轻量,所有策略集中管理,无额外类文件
两种模式共用一个枚举:单例 + 策略一体化
当某个策略本身也需全局唯一(比如日志处理器),可将两者融合:
public enum Logger {
FILE_LOGGER {
private final FileWriter writer = openFileWriter("app.log");
@Override
public void log(String msg) { writer.append(msg).append("\n"); }
},
CONSOLE_LOGGER {
@Override
public void log(String msg) { System.out.println(msg); }
};
<pre class="brush:php;toolbar:false;">public abstract void log(String msg);
private static FileWriter openFileWriter(String path) {
try {
return new FileWriter(path, true);
} catch (IOException e) {
throw new RuntimeException(e);
}
}}
这里每个 logger 是单例(JVM 保证每个枚举常量唯一),同时又是独立策略(各自实现 log 行为)。
- FILE_LOGGER 全局只有一个实例,且持有独占资源(FileWriter)
- CONSOLE_LOGGER 不持状态,但仍是明确、不可变的策略单元
- 调用方无需关心创建或缓存,直接用
Logger.FILE_LOGGER.log("start")
注意事项:别踩这些坑
枚举虽强大,但不是万能胶:
- 枚举实例在类加载时初始化,不能动态增删——适合固定策略集,不适合运行时配置变更
- 不能继承其他类(已隐式继承 Enum),但可以实现多个接口,增强复用性
- 若策略需要依赖注入(如 Spring Bean),枚举无法被容器管理,此时应改用普通类 + @Scope("singleton")
- 枚举字段建议设为 final,避免状态被意外修改,破坏单例语义
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











