单例模式是软件开发中一种常用的设计模式,其核心目标是确保某个类在整个程序运行期间只有一个实例存在,并提供一个全局访问点来获取该实例,这种模式在需要控制资源访问、避免重复创建对象(如数据库连接池、日志管理器、配置中心等)的场景中具有广泛应用,本文将详细介绍单例模式的核心原理、常见实现方式,并通过Java主函数示例展示其具体应用。

单例模式的核心原理
单例模式的实现需满足两个关键条件:唯一性与全局访问点。
- 唯一性:通过私有构造函数阻止外部类通过new关键字创建实例,确保类内部只能存在一个实例对象。
- 全局访问点:提供一个静态的公共方法(通常命名为
getInstance),允许外部程序获取该类的唯一实例。
单例模式还需考虑线程安全(多线程环境下防止创建多个实例)和延迟加载(避免在类加载时就创建实例,浪费资源)等实际开发中的问题。
单例模式的常见实现方式
根据线程安全和延迟加载的需求,单例模式有多种实现方式,以下是四种典型方案:
饿汉式:类加载时创建实例
实现逻辑:在类加载阶段就完成实例化,由JVM保证线程安全。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {} // 私有构造函数
public static EagerSingleton getInstance() {
return instance;
}
}
优点:实现简单,线程安全(JVM类加载机制保证)。
缺点:无法实现延迟加载,若实例未被使用,会造成资源浪费。

懒汉式:延迟加载,线程不安全
实现逻辑:首次调用getInstance时才创建实例,但未处理线程安全问题。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // 多线程环境下可能同时进入,创建多个实例
instance = new LazySingleton();
}
return instance;
}
}
优点:实现延迟加载,节省资源。
缺点:线程不安全,在多线程场景下可能失效。
双重检查锁定(DCL):线程安全 + 延迟加载
实现逻辑:通过synchronized关键字和双重检查确保线程安全,同时保持延迟加载。
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance; // volatile防止指令重排
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
if (instance == null) { // 第一次检查,避免不必要的同步
synchronized (DoubleCheckSingleton.class) {
if (instance == null) { // 第二次检查,确保唯一性
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
优点:线程安全,延迟加载,性能高效(仅第一次创建时同步)。
缺点:代码稍复杂,需注意volatile关键字防止指令重排序导致的线程安全问题。
静态内部类:推荐方案
实现逻辑:利用类加载机制保证线程安全,同时实现延迟加载。

public class StaticInnerSingleton {
private StaticInnerSingleton() {} // 私有构造函数
private static class SingletonHolder {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}
public static StaticInnerSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:线程安全(JVM保证类加载的原子性),延迟加载(SingletonHolder类仅在调用getInstance时加载),代码简洁。
缺点:无法传递参数创建实例(若需参数可通过工厂模式结合实现)。
Java主函数中测试单例模式
通过主函数可以直观验证单例模式的唯一性,以下以静态内部类和双重检查锁定为例,展示测试代码:
示例1:测试静态内部类单例
public class SingletonTest {
public static void main(String[] args) {
// 获取单例实例1
ConfigManager config1 = ConfigManager.getInstance();
config1.setConfig("database.url", "localhost:3306");
config1.setConfig("timeout", "5000");
// 获取单例实例2
ConfigManager config2 = ConfigManager.getInstance();
// 验证是否为同一实例
System.out.println("是否为同一实例: " + (config1 == config2)); // 输出 true
System.out.println("配置项timeout: " + config2.getConfig("timeout")); // 输出 5000
}
}
// 配置管理器单例(静态内部类实现)
class ConfigManager {
private Map<String, String> configMap = new HashMap<>();
private ConfigManager() {} // 私有构造函数
private static class ConfigHolder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
public static ConfigManager getInstance() {
return ConfigHolder.INSTANCE;
}
public void setConfig(String key, String value) {
configMap.put(key, value);
}
public String getConfig(String key) {
return configMap.get(key);
}
}
示例2:测试双重检查锁定单例
public class DCLSingletonTest {
public static void main(String[] args) {
// 多线程测试单例唯一性
Runnable task = () -> {
DoubleCheckSingleton instance = DoubleCheckSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ": " + instance.hashCode());
};
Thread t1 = new Thread(task, "线程1");
Thread t2 = new Thread(task, "线程2");
Thread t3 = new Thread(task, "线程3");
t1.start();
t2.start();
t3.start();
}
}
// 双重检查锁定单例
class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
输出结果:多线程环境下,所有线程获取的实例hashCode相同,验证了唯一性。
注意事项
- 防止反射破坏单例:通过私有构造函数中添加判断,避免反射调用构造方法创建新实例。
private Singleton() { if (instance != null) { throw new RuntimeException("单例模式不允许重复创建实例"); } } - 防止序列化破坏单例:实现
readResolve方法,返回已有实例而非新建对象。private Object readResolve() { return getInstance(); } - 选择合适的实现方式:若无延迟加载需求,优先使用饿汉式;若有延迟加载需求,静态内部类是最佳选择,双重检查锁定适用于高并发场景。
单例模式通过控制实例的创建和访问,有效节省了系统资源并确保了数据一致性,在实际开发中,需根据场景需求(线程安全、延迟加载、代码复杂度等)选择合适的实现方式,并通过主函数测试验证其正确性,掌握单例模式的核心原理和常见陷阱,是提升Java代码质量的重要一步。


















