Java 单例设计模式(Singleton Pattern)指南

📌 单例模式一句话理解

保证一个类只有一个实例,全局都能访问到它。

🎯 只需记住这3种实现(按推荐顺序)

1. 饿汉式(最简单,最常用)

java

csharp 复制代码
public class Singleton {
    // 1. 自己创建自己(static + final)
    private static final Singleton INSTANCE = new Singleton();
    
    // 2. 私有构造方法(关键!不让别人new)
    private Singleton() {}
    
    // 3. 提供获取方法
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

特点

  • ✅ 最简单
  • ✅ 线程安全
  • ❌ 启动时就创建,可能浪费资源

2. 双重检查锁(性能好)

java

csharp 复制代码
public class Singleton {
    // volatile防止指令重排
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                     // 第一次检查
            synchronized (Singleton.class) {        // 加锁
                if (instance == null) {             // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

特点

  • ✅ 延迟创建(需要时才创建)
  • ✅ 线程安全
  • ✅ 高性能

3. 静态内部类(推荐)

java

csharp 复制代码
public class Singleton {
    private Singleton() {}
    
    // 静态内部类
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

特点

  • ✅ 延迟创建
  • ✅ 线程安全
  • ✅ 实现优雅

🚀 快速选择指南

场景 推荐方案 原因
简单项目 饿汉式 最简单,不易出错
需要延迟创建 静态内部类 优雅,线程安全
性能要求高 双重检查锁 延迟创建,性能好
绝对要安全 枚举 防止反射破坏

💡 实际应用示例

1. 配置管理类(最常用场景)

java

typescript 复制代码
// 配置文件管理器
public class ConfigManager {
    private static final ConfigManager INSTANCE = new ConfigManager();
    
    private Map<String, String> configs = new HashMap<>();
    
    private ConfigManager() {
        // 加载配置文件
        configs.put("db.url", "localhost:3306");
        configs.put("db.user", "root");
        configs.put("app.name", "MyApp");
    }
    
    public static ConfigManager getInstance() {
        return INSTANCE;
    }
    
    public String getConfig(String key) {
        return configs.get(key);
    }
}

// 使用
public class Main {
    public static void main(String[] args) {
        // 全局都能访问同一个配置管理器
        String dbUrl = ConfigManager.getInstance().getConfig("db.url");
        System.out.println("数据库地址: " + dbUrl);
    }
}

2. 数据库连接池

java

csharp 复制代码
// 简易连接池
public class ConnectionPool {
    private static ConnectionPool instance;
    private List<Connection> connections;
    
    private ConnectionPool() {
        // 初始化连接池
        connections = new ArrayList<>();
        // ... 创建连接
    }
    
    public static synchronized ConnectionPool getInstance() {
        if (instance == null) {
            instance = new ConnectionPool();
        }
        return instance;
    }
    
    public Connection getConnection() {
        // 获取连接逻辑
        return null;
    }
}

⚠️ 常见错误

错误1:忘记私有构造器

java

csharp 复制代码
// ❌ 错误:构造器是public,别人可以new
public class WrongSingleton {
    private static WrongSingleton instance;
    
    public WrongSingleton() {  // 应该是 private!
    }
    
    // ...
}

错误2:非线程安全的懒汉式

java

csharp 复制代码
// ❌ 错误:多线程下可能创建多个实例
public class UnsafeSingleton {
    private static UnsafeSingleton instance;
    
    private UnsafeSingleton() {}
    
    public static UnsafeSingleton getInstance() {
        if (instance == null) {  // 线程不安全!
            instance = new UnsafeSingleton();
        }
        return instance;
    }
}

📝 记忆口诀

text

markdown 复制代码
单例模式三要素:
1. 私有构造器(不让new)
2. 静态实例(自己持有)
3. 静态方法(全局获取)

选择方案三选一:
要简单:饿汉式
要优雅:静态内部类
要性能:双重检查锁

🎯 实战建议

如果你刚学单例模式:

  1. 先用饿汉式 - 最简单不易错
  2. 理解原理后 - 尝试静态内部类
  3. 需要优化时 - 考虑双重检查锁

实际项目中:

  • 90% 的情况用饿汉式就够了
  • 配置类、工具类、连接池适合单例
  • 不要滥用,只有真正需要全局唯一时才用

记住:单例模式的核心就是控制对象的创建,确保全局只有一个实例!

相关推荐
Victor3568 小时前
MongoDB(95)如何在MongoDB中使用加密存储引擎?
后端
Victor3568 小时前
MongoDB(96)如何使用MongoDB的高级聚合功能?
后端
IT利刃出鞘9 小时前
Spring工具类--ObjectUtils的使用
java·后端·spring
2601_949816689 小时前
Spring boot启动原理及相关组件
数据库·spring boot·后端
GetcharZp15 小时前
告别 jq 噩梦!这款 JSON 神器 fx 让你在终端体验“丝滑”的数据操作
后端
小码哥_常17 小时前
告别臃肿!Elasticsearch平替Manticore登场
后端
苍何18 小时前
万字保姆级教程:Hermes+Kimi K2.6 打造7x24h Agent军团
后端
我叫黑大帅18 小时前
为什么map查找时间复杂度是O(1)?
后端·算法·面试
FreeCultureBoy20 小时前
用 phpbrew 管理 php 环境:从安装到多版本切换
后端·php
FreeCultureBoy20 小时前
用 jenv 管理 Java 环境:从安装 JDK 到多版本切换
后端