23种设计模式23种设计模式
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
  • 创建型模式

    • 单例模式 (Singleton)
    • 工厂方法模式 (Factory Method)
    • 抽象工厂模式 (Abstract Factory)
    • 建造者模式 (Builder)
    • 原型模式 (Prototype)

单例模式 (Singleton)

📖 通俗理解

想象一下,一个公司只能有一个CEO。不管你问谁"你们CEO是谁",得到的都是同一个人。

单例模式就是这样:保证一个类只有一个实例,并提供一个全局访问点。

🎯 解决什么问题?

有些对象我们只需要一个,比如:

  • 数据库连接池
  • 线程池
  • 配置文件对象
  • 日志对象

如果创建多个实例,会造成资源浪费或者数据不一致的问题。

🌰 生活中的例子

  • 太阳:地球只有一个太阳
  • 班长:一个班只有一个班长
  • 打印机:办公室共用一台打印机
  • 任务管理器:Windows 只能打开一个任务管理器

💻 Java 代码实现

方式一:饿汉式(推荐)

类加载时就创建实例,简单安全。

/**
 * 饿汉式单例
 * 优点:简单,线程安全
 * 缺点:类加载就创建,可能造成资源浪费
 */
public class Singleton {
    
    // 1. 私有静态实例,类加载时就创建
    private static final Singleton INSTANCE = new Singleton();
    
    // 2. 私有构造方法,防止外部 new
    private Singleton() {
        System.out.println("单例对象被创建了!");
    }
    
    // 3. 公共静态方法,返回唯一实例
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    public void doSomething() {
        System.out.println("单例对象在工作...");
    }
}

测试代码:

public class Test {
    public static void main(String[] args) {
        // 获取单例对象
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        
        // 验证是同一个对象
        System.out.println(s1 == s2);  // true
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());  // hashCode 相同
    }
}

方式二:懒汉式(双重检查锁)

第一次使用时才创建实例,节省资源。

/**
 * 懒汉式单例(双重检查锁)
 * 优点:延迟加载,节省资源
 * 注意:必须用 volatile 修饰,防止指令重排序
 */
public class LazySingleton {
    
    // volatile 防止指令重排序
    private static volatile LazySingleton instance;
    
    private LazySingleton() {
        System.out.println("懒汉式单例被创建了!");
    }
    
    public static LazySingleton getInstance() {
        // 第一次检查,避免不必要的同步
        if (instance == null) {
            synchronized (LazySingleton.class) {
                // 第二次检查,防止多线程问题
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

方式三:静态内部类(最推荐)⭐

结合了饿汉式和懒汉式的优点!

/**
 * 静态内部类单例(推荐使用)
 * 优点:
 * 1. 延迟加载(调用 getInstance 时才加载内部类)
 * 2. 线程安全(JVM 保证类加载的线程安全)
 * 3. 代码简洁
 */
public class BestSingleton {
    
    private BestSingleton() {
        System.out.println("静态内部类单例被创建了!");
    }
    
    // 静态内部类
    private static class SingletonHolder {
        private static final BestSingleton INSTANCE = new BestSingleton();
    }
    
    public static BestSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

方式四:枚举(最简洁)

《Effective Java》作者推荐的方式!

/**
 * 枚举单例
 * 优点:
 * 1. 代码最简洁
 * 2. 天然防止反射和序列化破坏单例
 */
public enum EnumSingleton {
    
    INSTANCE;
    
    public void doSomething() {
        System.out.println("枚举单例在工作...");
    }
}

// 使用方式
// EnumSingleton.INSTANCE.doSomething();

🔥 实战案例:配置文件管理器

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 配置文件管理器(单例模式)
 * 整个应用只需要一个配置对象
 */
public class ConfigManager {
    
    private Properties properties;
    
    private static class Holder {
        private static final ConfigManager INSTANCE = new ConfigManager();
    }
    
    private ConfigManager() {
        properties = new Properties();
        try {
            InputStream is = getClass().getClassLoader()
                    .getResourceAsStream("config.properties");
            if (is != null) {
                properties.load(is);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static ConfigManager getInstance() {
        return Holder.INSTANCE;
    }
    
    public String getProperty(String key) {
        return properties.getProperty(key);
    }
    
    public String getProperty(String key, String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }
}

使用方式:

public class Test {
    public static void main(String[] args) {
        // 在任何地方获取配置
        ConfigManager config = ConfigManager.getInstance();
        
        String dbUrl = config.getProperty("db.url");
        String dbUser = config.getProperty("db.user", "root");
        
        System.out.println("数据库地址:" + dbUrl);
        System.out.println("数据库用户:" + dbUser);
    }
}

📊 对比总结

实现方式线程安全延迟加载防反射破坏推荐指数
饿汉式✅❌❌⭐⭐⭐
懒汉式(双重检查锁)✅✅❌⭐⭐⭐
静态内部类✅✅❌⭐⭐⭐⭐
枚举✅❌✅⭐⭐⭐⭐⭐

✅ 适用场景

  • 需要频繁创建销毁的对象
  • 创建对象耗时过长或耗资源过多
  • 工具类对象
  • 频繁访问数据库或文件的对象

⚠️ 注意事项

  1. 私有化构造方法:防止外部 new 对象
  2. 线程安全:多线程环境下要保证只创建一个实例
  3. 反射和序列化:可能会破坏单例,枚举方式可以解决

小结

单例模式是最简单的设计模式,核心就是:私有构造 + 静态方法返回唯一实例。

推荐使用 静态内部类 或 枚举 方式实现。

👉 下一篇:工厂方法模式

Next
工厂方法模式 (Factory Method)