本文目录导读:

我来为你提供一个完整的枚举单例案例,并详细解释其特点。
基础枚举单例
public enum Singleton {
INSTANCE;
private String config;
// 构造函数(枚举的构造器默认private)
Singleton() {
config = "默认配置";
// 可以在这里进行初始化操作
System.out.println("枚举单例被初始化");
}
public void doSomething() {
System.out.println("执行方法,配置为:" + config);
}
public void setConfig(String config) {
this.config = config;
}
}
测试类
public class EnumSingletonTest {
public static void main(String[] args) {
// 使用单例
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println("是否为同一个实例: " + (singleton1 == singleton2));
System.out.println("hashCode 是否相同: " +
(singleton1.hashCode() == singleton2.hashCode()));
singleton1.doSomething();
// 修改配置
singleton1.setConfig("新的配置");
singleton2.doSomething(); // 验证配置已更新
// 测试多线程环境
testMultiThread();
}
private static void testMultiThread() {
Runnable task = () -> {
Singleton singleton = Singleton.INSTANCE;
System.out.println(Thread.currentThread().getName() +
" - 实例: " + singleton.hashCode());
};
for (int i = 0; i < 5; i++) {
new Thread(task, "线程-" + i).start();
}
}
}
带数据和方法的枚举单例
import java.io.*;
public enum DatabaseConnection {
INSTANCE;
// 模拟数据库连接池
private ConnectionPool pool;
private int connectionCount;
DatabaseConnection() {
System.out.println("初始化数据库连接池...");
pool = new ConnectionPool();
connectionCount = 0;
}
// 模拟连接池类
static class ConnectionPool {
void connect() {
System.out.println("创建数据库连接");
}
void disconnect() {
System.out.println("关闭数据库连接");
}
}
public synchronized void connect() {
connectionCount++;
pool.connect();
}
public synchronized void disconnect() {
if (connectionCount > 0) {
connectionCount--;
}
pool.disconnect();
}
public int getConnectionCount() {
return connectionCount;
}
public void showStatus() {
System.out.println("当前活跃连接数: " + connectionCount);
}
}
处理序列化的枚举单例
import java.io.*;
public enum SerializableSingleton implements Serializable {
INSTANCE;
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
// 枚举天然防止序列化破坏单例,无需实现readResolve()
// 但可以重写readResolve来确保反序列化后返回相同实例
private Object readResolve() {
return INSTANCE;
}
// 测试序列化行为
public static void main(String[] args) throws Exception {
SerializableSingleton instance = SerializableSingleton.INSTANCE;
instance.setData("测试数据");
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(instance);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
SerializableSingleton deserialized = (SerializableSingleton) ois.readObject();
ois.close();
System.out.println("序列化前后是否相同: " + (instance == deserialized));
System.out.println("反序列化后数据: " + deserialized.getData());
}
}
带延迟初始化的枚举单例(复杂场景)
public enum LazySingleton {
INSTANCE;
// 私有静态内部类用于延迟加载复杂资源
private enum ResourceHolder {
INSTANCE;
private ComplexResource resource;
ResourceHolder() {
System.out.println("延迟加载复杂资源...");
resource = new ComplexResource();
}
}
public static class ComplexResource {
private String name;
ComplexResource() {
name = "复杂资源-" + System.currentTimeMillis();
}
public void process() {
System.out.println("处理资源: " + name);
}
}
public ComplexResource getResource() {
return ResourceHolder.INSTANCE.resource;
}
}
完整的应用示例
public class CompleteExample {
// 配置管理单例
public enum ConfigManager {
INSTANCE;
private Properties properties = new Properties();
ConfigManager() {
loadDefaultConfig();
}
private void loadDefaultConfig() {
properties.setProperty("app.name", "我的应用");
properties.setProperty("app.version", "1.0.0");
properties.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb");
properties.setProperty("database.user", "root");
}
public String getProperty(String key) {
return properties.getProperty(key);
}
public void setProperty(String key, String value) {
properties.setProperty(key, value);
}
public void displayConfig() {
System.out.println("=== 应用配置 ===");
properties.forEach((k, v) ->
System.out.println(k + " = " + v));
}
}
public static void main(String[] args) {
// 使用配置管理单例
System.out.println("应用名: " + ConfigManager.INSTANCE.getProperty("app.name"));
System.out.println("数据库URL: " + ConfigManager.INSTANCE.getProperty("database.url"));
// 修改配置
ConfigManager.INSTANCE.setProperty("app.version", "2.0.0");
System.out.println("更新后版本: " + ConfigManager.INSTANCE.getProperty("app.version"));
// 显示所有配置
ConfigManager.INSTANCE.displayConfig();
}
}
防止反射破坏的完整示例
import java.lang.reflect.Constructor;
public enum SafeSingleton implements Cloneable {
INSTANCE;
private String name;
// 防止克隆
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("单例不能被克隆");
}
// 测试反射攻击
public static void testReflection() {
try {
Constructor<SafeSingleton> constructor =
SafeSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
SafeSingleton anotherInstance = constructor.newInstance();
System.out.println("反射创建的实例: " + anotherInstance);
} catch (Exception e) {
System.out.println("反射攻击失败: " + e.getMessage());
}
}
public static void main(String[] args) {
SafeSingleton instance1 = SafeSingleton.INSTANCE;
SafeSingleton instance2 = SafeSingleton.INSTANCE;
System.out.println("两个实例是否相同: " + (instance1 == instance2));
// 测试反射攻击
testReflection();
// 测试克隆
try {
instance1.clone();
} catch (Exception e) {
System.out.println("克隆失败: " + e.getMessage());
}
}
}
枚举单例的核心优势
- 线程安全:枚举实例的创建由JVM保证,天然线程安全
- 防反射攻击:枚举没有公共构造器,反射无法创建新实例
- 防序列化破坏:枚举反序列化时自动复用现有实例
- 防克隆:枚举不能使用clone()方法创建新实例
- 简洁直观:代码简洁,易于理解和使用
最佳实践建议
// 推荐的枚举单例模式
public enum RecommendedSingleton {
INSTANCE;
// 私有字段
private DatabaseConnector db;
private CacheManager cache;
// 构造函数中初始化所有资源
RecommendedSingleton() {
// 初始化资源
db = new DatabaseConnector();
cache = new CacheManager();
}
// 提供业务方法
public void connectDB() {
db.connect();
}
public Object getFromCache(String key) {
return cache.get(key);
}
}
枚举单例是目前公认的最安全的单例实现方式之一,它很好地解决了传统单例模式中存在的线程安全、反射攻击、序列化破坏等问题。