设计模式是 Java 程序员从「会写代码」迈向「会设计代码」的必经之路。面试常考、源码常见,但不用一口气学 23 种,先把最常用的 6 种搞懂,工作中够用。

一、单例模式——全局只用一个对象

保证一个类在整个应用中只有一个实例,数据库连接池、配置管理器等场景常用。

饿汉式(线程安全,推荐简单场景)

public class Singleton {
    // 类加载时直接创建,天然线程安全
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}  // 私有构造

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

优点: 写法简单、线程安全。缺点: 不管用不用都创建。

懒汉式(双重检测,推荐标准写法)

public class Singleton {
    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;
    }
}

volatile 关键词不能省,防止指令重排导致拿到未初始化完成的对象。

静态内部类(最优雅)

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;  // 调用时才加载内部类,实现懒加载
    }
}

面试推荐用这个,既懒加载又线程安全,还不用写 synchronized。

枚举单例(防反射/防序列化)

public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {
        System.out.println("枚举单例执行");
    }
}
// 调用:SingletonEnum.INSTANCE.doSomething();

唯一能防止反射攻击和反序列化破坏的单例方式。

二、工厂模式——把创建对象交给工厂

简单工厂

// 产品接口
interface Product {
    void work();
}

class AProduct implements Product {
    public void work() { System.out.println("产品A工作"); }
}

class BProduct implements Product {
    public void work() { System.out.println("产品B工作"); }
}

// 工厂类:根据参数返回对应产品
class SimpleFactory {
    public static Product create(String type) {
        switch (type) {
            case "A": return new AProduct();
            case "B": return new BProduct();
            default: throw new IllegalArgumentException("未知类型");
        }
    }
}

// 使用
Product p = SimpleFactory.create("A");
p.work();

工厂方法(每个产品一个工厂)

interface Factory {
    Product create();
}

class AFactory implements Factory {
    public Product create() { return new AProduct(); }
}

class BFactory implements Factory {
    public Product create() { return new BProduct(); }
}

// 使用
Factory factory = new AFactory();
Product p = factory.create();

简单工厂 vs 工厂方法: 后者更符合开闭原则,新增产品不修改旧代码,加个新工厂类就行。

三、策略模式——把算法封装起来

业务中最常用的设计模式之一,代替 if-else 的利器

// 策略接口
interface PaymentStrategy {
    void pay(BigDecimal amount);
}

// 具体策略
class AlipayStrategy implements PaymentStrategy {
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付: " + amount + "元");
    }
}

class WechatStrategy implements PaymentStrategy {
    public void pay(BigDecimal amount) {
        System.out.println("微信支付: " + amount + "元");
    }
}

class CardStrategy implements PaymentStrategy {
    public void pay(BigDecimal amount) {
        System.out.println("银行卡支付: " + amount + "元");
    }
}

// 上下文(使用策略的对象)
class PaymentContext {
    private PaymentStrategy strategy;

    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePay(BigDecimal amount) {
        strategy.pay(amount);  // 调用策略
    }
}

// 使用
PaymentContext ctx = new PaymentContext(new AlipayStrategy());
ctx.executePay(new BigDecimal("99.9"));

对比 if-else: 新增支付方式只需要加新类,不需要改现有代码。

四、代理模式——给对象加一层控制

Spring AOP、MyBatis 的 Mapper 接口、延迟加载等底层都用到了代理。

静态代理

// 接口
interface UserService {
    void saveUser(String name);
}

// 目标类
class UserServiceImpl implements UserService {
    public void saveUser(String name) {
        System.out.println("保存用户: " + name);
    }
}

// 代理类(加日志)
class UserServiceProxy implements UserService {
    private UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    public void saveUser(String name) {
        System.out.println("[日志] saveUser 开始,参数: " + name);
        target.saveUser(name);
        System.out.println("[日志] saveUser 结束");
    }
}

// 使用
UserService service = new UserServiceProxy(new UserServiceImpl());
service.saveUser("张三");

JDK 动态代理(最常用)

UserService target = new UserServiceImpl();

UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    (proxyObj, method, args) -> {
        System.out.println("[代理] " + method.getName() + " 开始");
        Object result = method.invoke(target, args);
        System.out.println("[代理] " + method.getName() + " 结束");
        return result;
    }
);

proxy.saveUser("张三");

Spring AOP 用法

@Aspect
@Component
public class LogAspect {

    @Around("execution(* com.zhang.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[AOP] 前置通知");
        Object result = pjp.proceed();  // 执行目标方法
        System.out.println("[AOP] 后置通知");
        return result;
    }
}

AOP 底层就是动态代理——有接口用 JDK 代理,没有接口用 CGLIB 代理。

五、模板方法模式——固定流程,开放步骤

适合「流程固定、步骤可变」的场景。

// 抽象类:定义流程骨架
abstract class AbstractDataImporter {

    // 模板方法(final 防止子类修改流程)
    public final void importData() {
        openFile();
        parseData();
        validateData();
        saveData();
        closeFile();
    }

    // 固定步骤(子类不需要改)
    private void openFile() { System.out.println("打开文件"); }
    private void closeFile() { System.out.println("关闭文件"); }

    // 可变步骤(子类实现)
    protected abstract void parseData();
    protected abstract void validateData();
    protected abstract void saveData();
}

// 具体实现
class ExcelImporter extends AbstractDataImporter {
    protected void parseData() { System.out.println("解析 Excel 数据"); }
    protected void validateData() { System.out.println("校验 Excel 数据格式"); }
    protected void saveData() { System.out.println("保存 Excel 数据到数据库"); }
}

class CsvImporter extends AbstractDataImporter {
    protected void parseData() { System.out.println("解析 CSV 数据"); }
    protected void validateData() { System.out.println("校验 CSV 数据格式"); }
    protected void saveData() { System.out.println("保存 CSV 数据到数据库"); }
}

JDK 中 Arrays.sort()InputStream.read() 都是模板方法模式。

六、观察者模式——事件通知机制

一个对象状态变化,自动通知其他依赖对象。消息队列、事件监听、GUI 事件都是观察者模式。

// 观察者接口
interface Observer {
    void update(String message);
}

// 被观察者(主题)
class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer obs : observers) {
            obs.update(message);
        }
    }
}

// 具体观察者
class EmailObserver implements Observer {
    private String name;

    public EmailObserver(String name) { this.name = name; }

    public void update(String message) {
        System.out.println(name + " 收到邮件通知: " + message);
    }
}

class SmsObserver implements Observer {
    private String name;

    public SmsObserver(String name) { this.name = name; }

    public void update(String message) {
        System.out.println(name + " 收到短信通知: " + message);
    }
}

// 使用
Subject subject = new Subject();
subject.attach(new EmailObserver("张三"));
subject.attach(new SmsObserver("李四"));

subject.notifyObservers("订单已发货");  // 所有观察者自动收到通知

Spring 事件监听(实际开发更常用)

// 定义事件
public class OrderEvent extends ApplicationEvent {
    private String orderNo;
    public OrderEvent(Object source, String orderNo) {
        super(source);
        this.orderNo = orderNo;
    }
    public String getOrderNo() { return orderNo; }
}

// 监听器
@Component
public class SmsListener implements ApplicationListener<OrderEvent> {
    public void onApplicationEvent(OrderEvent event) {
        System.out.println("发送短信通知:订单 " + event.getOrderNo() + " 已创建");
    }
}

@Component
public class EmailListener implements ApplicationListener<OrderEvent> {
    public void onApplicationEvent(OrderEvent event) {
        System.out.println("发送邮件通知:订单 " + event.getOrderNo() + " 已创建");
    }
}

// 发布事件
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void createOrder(String orderNo) {
        System.out.println("创建订单: " + orderNo);
        publisher.publishEvent(new OrderEvent(this, orderNo));
        // 监听器自动执行,不影响主流程
    }
}

七、设计模式选择指南

模式 适用场景 核心思想
单例 全局只需要一个对象(配置、连接池) 控制实例数量
工厂 创建对象逻辑复杂或需要解耦 分离创建和使用
策略 多种行为可互换,避免 if-else 封装变化
代理 给对象增强功能(日志、事务、权限) 控制访问
模板方法 流程骨架固定,部分步骤可变 复用流程
观察者 一对多依赖关系,状态变化通知 解耦通知

八、如何在项目中运用

不要为了用设计模式而用设计模式。遇到以下情况再考虑:

if-else 太多 → 考虑策略模式
需要加日志/事务 → 考虑代理模式
流程固定但步骤不同 → 模板方法模式
全局只用一个实例 → 单例模式
对象创建复杂 → 工厂模式
需要事件通知 → 观察者模式

设计模式的最高境界是:用了但感觉没用——代码自然、干净、好维护。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。