推荐使用静态内部类或枚举实现单例:静态内部类利用jvm类加载机制实现线程安全、懒加载、防反射;枚举则天然具备线程安全、防反射与反序列化攻击能力,且语法简洁。

直接用静态内部类或枚举实现,既线程安全又支持懒加载,还能防反射和反序列化攻击。
静态内部类方式(推荐日常使用)
利用 JVM 类加载机制的线程安全性,外部类加载时不初始化内部类,只有首次调用 getInstance() 时才触发内部类加载和实例创建,天然懒加载且无锁。
- 写法简洁,不依赖 volatile 或 synchronized,无性能开销
- 实例真正按需创建,适合配置加载耗时、或可能不被使用的场景
- 构造函数私有,无法被反射绕过(除非暴力 setAccessible,但生产环境应禁用)
示例:
public class ConfigManager {private ConfigManager() { load(); }
private void load() { /* 读取 application.properties 或 YAML */ }
private static class Holder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
public static ConfigManager getInstance() {
return Holder.INSTANCE;
}
public String get(String key) { /* 返回配置值 */ }
}
枚举单例方式(推荐高安全要求场景)
JVM 保证枚举实例的创建是原子且不可变的,天生线程安全,同时能抵抗反射和反序列化破坏,是《Effective Java》明确推荐的方式。
- 一行定义,语法极简,无需额外防护逻辑
- 即使通过反射调用 Constructor.newInstance(),JVM 也会抛出 IllegalArgumentException
- 序列化/反序列化自动维护单例性,无需实现 readResolve()
示例:
public enum ConfigManager {INSTANCE;
private Properties props;
ConfigManager() {
props = new Properties();
try (InputStream is = getClass().getClassLoader().getResourceAsStream("config.properties")) {
props.load(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String get(String key) {
return props.getProperty(key);
}
}
避免使用的写法
基础懒汉式(无同步)和简单 synchronized 方法都有明显缺陷:前者多线程下可能生成多个实例;后者每次调用都加锁,严重拖慢高频访问场景(如配置频繁读取)。
- 双重检查锁(DCL)虽可行,但必须搭配 volatile 和两次判空,稍有遗漏就会因指令重排序导致未完全初始化的对象被返回
- 饿汉式在配置文件缺失或加载失败时,会导致类加载失败,异常暴露过早,不利于容错和动态配置热加载
配套建议
配置类不是孤立存在,还需配合实际使用习惯提升健壮性:
- 加载失败时抛运行时异常(如 IllegalStateException),而非静默 fallback,避免隐藏问题
- 提供 reload() 方法(加锁保护),支持配置热更新(注意线程可见性,可用 volatile 字段或 AtomicReference 包装 config 对象)
- 若配置含敏感信息(如密码),避免 toString() 泄露,重写该方法或使用日志脱敏策略











