💡 摘要:你是否曾需要确保一个类只有一个实例?是否厌倦了到处使用new关键字创建对象?是否想写出更灵活、更易维护的代码?
别担心,设计模式是软件设计中经过验证的最佳实践解决方案。单例模式和工厂模式作为最常用的两种创建型模式,能帮你解决对象创建和管理的各种问题。
本文将带你从单例模式的各种实现讲起,理解如何保证一个类只有一个实例。然后深入工厂模式的多种变体,学习如何优雅地创建对象。最后通过实战案例展示这两种模式在框架开发、业务逻辑中的实际应用。从线程安全到性能优化,从简单实现到高级技巧,让你全面掌握这两种核心设计模式。文末附常见陷阱和面试高频问题,助你写出更专业的代码。
一、单例模式:确保唯一实例
1. 单例模式的核心概念
单例模式定义:保证一个类只有一个实例,并提供一个全局访问点。
适用场景:
- ✅ 需要频繁创建和销毁的对象
- ✅ 创建对象耗时过多或耗资源过多
- ✅ 工具类对象
- ✅ 频繁访问数据库或文件的对象
2. 饿汉式单例(Eager Initialization)
最简单的实现:
java
public class EagerSingleton {
// 类加载时就创建实例
private static final EagerSingleton INSTANCE = new EagerSingleton();
// 私有构造器,防止外部创建实例
private EagerSingleton() {
// 防止通过反射创建实例
if (INSTANCE != null) {
throw new RuntimeException("单例模式禁止反射创建");
}
}
// 全局访问点
public static EagerSingleton getInstance() {
return INSTANCE;
}
// 业务方法
public void doSomething() {
System.out.println("饿汉式单例执行操作");
}
}
优点:实现简单,线程安全
缺点:类加载时就创建实例,可能造成资源浪费
3. 懒汉式单例(Lazy Initialization)
基础懒汉式(线程不安全):
java
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 线程不安全版本
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
同步懒汉式(线程安全但性能差):
java
public class SynchronizedSingleton {
private static SynchronizedSingleton instance;
private SynchronizedSingleton() {}
// 同步方法,线程安全但性能差
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}
4. 双重检查锁(Double-Checked Locking)
最优的懒汉式实现:
java
public class DoubleCheckedSingleton {
// 使用volatile防止指令重排序
private static volatile DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {
// 防止反射攻击
if (instance != null) {
throw new RuntimeException("禁止反射创建单例");
}
}
public static DoubleCheckedSingleton getInstance() {
// 第一次检查,避免不必要的同步
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
// 第二次检查,确保只有一个线程创建实例
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
volatile的重要性:
java
// 没有volatile时可能发生的问题:
// instance = new Singleton() 分为三步:
// 1. 分配内存空间
// 2. 初始化对象
// 3. 将instance指向内存空间
// 可能发生指令重排序:1→3→2
// 其他线程可能在对象未完全初始化时看到instance不为null
5. 静态内部类实现(推荐方式)
线程安全且懒加载:
java
public class InnerClassSingleton {
private InnerClassSingleton() {
// 防止反射攻击
if (SingletonHolder.INSTANCE != null) {
throw new RuntimeException("禁止反射创建单例");
}
}
// 静态内部类在第一次使用时加载
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
// 防止反序列化破坏单例
protected Object readResolve() {
return getInstance();
}
}
优点:线程安全、懒加载、实现简单
6. 枚举单例(最佳实践)
Joshua Bloch推荐的方式:
java
public enum EnumSingleton {
INSTANCE;
// 添加业务方法
private int counter = 0;
public void doSomething() {
System.out.println("枚举单例执行操作,计数器: " + (++counter));
}
public int getCounter() {
return counter;
}
}
// 使用示例
public class EnumSingletonClient {
public static void main(String[] args) {
EnumSingleton.INSTANCE.doSomething(); // 计数器: 1
EnumSingleton.INSTANCE.doSomething(); // 计数器: 2
// 线程安全测试
for (int i = 0; i < 10; i++) {
new Thread(() -> {
EnumSingleton.INSTANCE.doSomething();
}).start();
}
}
}
枚举单例的优势:
- ✅ 绝对防止多次实例化
- ✅ 自动处理序列化问题
- ✅ 线程安全
- ✅ 防止反射攻击
二、工厂模式:优雅的对象创建
1. 简单工厂模式(Simple Factory)
基础实现:
java
// 产品接口
public interface Product {
void use();
}
// 具体产品
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 简单工厂
public class SimpleFactory {
public static Product createProduct(String type) {
switch (type) {
case "A":
return new ConcreteProductA();
case "B":
return new ConcreteProductB();
default:
throw new IllegalArgumentException("未知产品类型: " + type);
}
}
}
// 使用示例
public class SimpleFactoryClient {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.use(); // 使用产品A
Product productB = SimpleFactory.createProduct("B");
productB.use(); // 使用产品B
}
}
2. 工厂方法模式(Factory Method)
定义创建接口:
java
// 抽象创建者
public abstract class Creator {
// 工厂方法
public abstract Product createProduct();
// 业务方法
public void doSomething() {
Product product = createProduct();
product.use();
}
}
// 具体创建者
public class ConcreteCreatorA extends Creator {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
public class ConcreteCreatorB extends Creator {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 使用示例
public class FactoryMethodClient {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
creatorA.doSomething(); // 使用产品A
Creator creatorB = new ConcreteCreatorB();
creatorB.doSomething(); // 使用产品B
}
}
3. 抽象工厂模式(Abstract Factory)
产品族创建:
java
// 抽象产品族
public interface Button {
void render();
}
public interface TextField {
void input();
}
// 具体产品 - Windows风格
public class WindowsButton implements Button {
@Override
public void render() {
System.out.println("渲染Windows风格按钮");
}
}
public class WindowsTextField implements TextField {
@Override
public void input() {
System.out.println("Windows文本框输入");
}
}
// 具体产品 - Mac风格
public class MacButton implements Button {
@Override
public void render() {
System.out.println("渲染Mac风格按钮");
}
}
public class MacTextField implements TextField {
@Override
public void input() {
System.out.println("Mac文本框输入");
}
}
// 抽象工厂
public interface GUIFactory {
Button createButton();
TextField createTextField();
}
// 具体工厂
public class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextField createTextField() {
return new WindowsTextField();
}
}
public class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public TextField createTextField() {
return new MacTextField();
}
}
// 使用示例
public class AbstractFactoryClient {
private Button button;
private TextField textField;
public AbstractFactoryClient(GUIFactory factory) {
button = factory.createButton();
textField = factory.createTextField();
}
public void renderUI() {
button.render();
textField.input();
}
public static void main(String[] args) {
// 创建Windows风格UI
GUIFactory windowsFactory = new WindowsFactory();
AbstractFactoryClient windowsClient = new AbstractFactoryClient(windowsFactory);
windowsClient.renderUI();
// 创建Mac风格UI
GUIFactory macFactory = new MacFactory();
AbstractFactoryClient macClient = new AbstractFactoryClient(macFactory);
macClient.renderUI();
}
}
三、实战应用案例
1. 数据库连接池(单例模式)
连接池单例实现:
java
public class ConnectionPool {
private static final int MAX_POOL_SIZE = 10;
private static ConnectionPool instance;
private final List<Connection> connections;
private final String url;
private final String user;
private final String password;
private ConnectionPool(String url, String user, String password) {
this.url = url;
this.user = user;
this.password = password;
this.connections = new ArrayList<>(MAX_POOL_SIZE);
initializePool();
}
// 双重检查锁实现
public static ConnectionPool getInstance(String url, String user, String password) {
if (instance == null) {
synchronized (ConnectionPool.class) {
if (instance == null) {
instance = new ConnectionPool(url, user, password);
}
}
}
return instance;
}
private void initializePool() {
try {
for (int i = 0; i < MAX_POOL_SIZE; i++) {
Connection conn = DriverManager.getConnection(url, user, password);
connections.add(conn);
}
} catch (SQLException e) {
throw new RuntimeException("初始化连接池失败", e);
}
}
public synchronized Connection getConnection() {
while (connections.isEmpty()) {
try {
wait(); // 等待连接释放
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("等待连接中断", e);
}
}
return connections.remove(connections.size() - 1);
}
public synchronized void releaseConnection(Connection conn) {
connections.add(conn);
notifyAll(); // 通知等待的线程
}
}
2. 支付系统(工厂模式)
支付方式工厂:
java
// 支付接口
public interface Payment {
boolean pay(double amount);
String getType();
}
// 具体支付方式
public class AlipayPayment implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("支付宝支付: " + amount + "元");
// 调用支付宝API
return true;
}
@Override
public String getType() {
return "alipay";
}
}
public class WechatPayment implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("微信支付: " + amount + "元");
// 调用微信API
return true;
}
@Override
public String getType() {
return "wechat";
}
}
public class BankCardPayment implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("银行卡支付: " + amount + "元");
// 调用银行API
return true;
}
@Override
public String getType() {
return "bankcard";
}
}
// 支付工厂
public class PaymentFactory {
public static Payment createPayment(String type) {
switch (type.toLowerCase()) {
case "alipay":
return new AlipayPayment();
case "wechat":
return new WechatPayment();
case "bankcard":
return new BankCardPayment();
default:
throw new IllegalArgumentException("不支持的支付方式: " + type);
}
}
// 通过配置文件创建
public static Payment createPaymentFromConfig() {
Properties props = loadPaymentConfig();
String defaultPayment = props.getProperty("default.payment");
return createPayment(defaultPayment);
}
private static Properties loadPaymentConfig() {
Properties props = new Properties();
try (InputStream input = PaymentFactory.class.getResourceAsStream("/payment.properties")) {
props.load(input);
} catch (IOException e) {
throw new RuntimeException("加载支付配置失败", e);
}
return props;
}
}
// 使用示例
public class PaymentSystem {
public static void main(String[] args) {
// 根据用户选择创建支付方式
Payment payment = PaymentFactory.createPayment("alipay");
payment.pay(100.0);
// 根据配置创建默认支付方式
Payment defaultPayment = PaymentFactory.createPaymentFromConfig();
defaultPayment.pay(200.0);
}
}
四、模式对比与选择指南
1. 单例模式变体对比
| 实现方式 | 线程安全 | 懒加载 | 防止反射 | 防止序列化 | 性能 |
| 饿汉式 | ✅ | ❌ | ❌ | ❌ | ⭐⭐⭐⭐⭐ |
| 同步懒汉式 | ✅ | ✅ | ❌ | ❌ | ⭐⭐ |
| 双重检查锁 | ✅ | ✅ | ✅ | ❌ | ⭐⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | ✅ | ❌ | ⭐⭐⭐⭐⭐ |
| 枚举 | ✅ | ❌ | ✅ | ✅ | ⭐⭐⭐⭐⭐ |
2. 工厂模式变体对比
| 模式 | 适用场景 | 优点 | 缺点 |
| 简单工厂 | 产品类型较少,变化不频繁 | 简单易用 | 违反开闭原则 |
| 工厂方法 | 产品类型较多,需要扩展 | 符合开闭原则 | 类数量增多 |
| 抽象工厂 | 产品族创建,需要保证兼容性 | 保证产品兼容性 | 扩展产品族困难 |
五、常见陷阱与最佳实践
1. 单例模式陷阱
反射攻击防护:
java
public class SafeSingleton {
private static volatile SafeSingleton instance;
private SafeSingleton() {
// 防止反射攻击
if (instance != null) {
throw new RuntimeException("禁止通过反射创建单例");
}
}
public static SafeSingleton getInstance() {
// 双重检查锁
if (instance == null) {
synchronized (SafeSingleton.class) {
if (instance == null) {
instance = new SafeSingleton();
}
}
}
return instance;
}
// 防止反序列化创建新实例
protected Object readResolve() {
return getInstance();
}
}
2. 工厂模式最佳实践
使用依赖注入:
java
// 使用Spring框架的依赖注入
@Component
public class PaymentService {
private final Map<String, Payment> paymentStrategies;
// 通过构造函数注入所有支付策略
public PaymentService(List<Payment> payments) {
paymentStrategies = payments.stream()
.collect(Collectors.toMap(Payment::getType, Function.identity()));
}
public boolean processPayment(String type, double amount) {
Payment payment = paymentStrategies.get(type);
if (payment == null) {
throw new IllegalArgumentException("不支持的支付方式: " + type);
}
return payment.pay(amount);
}
}
六、总结:模式选择指南
1. 单例模式使用场景
- ✅ 配置信息管理
- ✅ 数据库连接池
- ✅ 日志记录器
- ✅ 应用计数器
- ✅ 线程池管理
2. 工厂模式使用场景
- ✅ 对象创建逻辑复杂
- ✅ 需要统一创建接口
- ✅ 需要动态选择实现类
- ✅ 系统需要支持多种产品族
七、面试高频问题
❓1. 单例模式有哪些实现方式?哪种最好?
答:饿汉式、懒汉式、双重检查锁、静态内部类、枚举。枚举方式最好,因为它线程安全、防止反射和序列化攻击、实现简单。
❓2. 双重检查锁为什么要用volatile?
答:防止指令重排序,避免其他线程看到未完全初始化的实例。
❓3. 工厂方法模式和抽象工厂模式有什么区别?
答:工厂方法模式针对一个产品等级结构,抽象工厂模式针对多个产品等级结构(产品族)。
❓4. 如何防止单例模式被反射破坏?
答:在私有构造器中检查实例是否已存在,如果已存在则抛出异常。
❓5. 什么情况下应该使用工厂模式?
答:当对象创建逻辑复杂,需要统一管理创建过程,或者需要动态选择具体实现类时。