Java程序员常用设计模式详解(实战版)
23种经典设计模式是基础但实际工作中我们常用的也就10种左右。今天就按“创建型、结构型、行为型”三大分类结合Java实战场景把每个常用模式讲透新手也能快速上手。一、先搞懂设计模式的核心意义很多新手会问“不用设计模式代码也能跑为什么非要学”举个简单例子如果写一个用户登录功能不考虑设计模式可能会把“验证逻辑、数据库查询、返回结果处理”全写在一个方法里——后续要加“验证码验证”“第三方登录”只能硬改原代码改着改着就成了“祖传屎山”。而设计模式的核心就是“解耦”让代码的各个模块各司其职修改一个模块不影响其他模块同时提升复用性。记住一句话设计模式不是“炫技”是解决实际问题的工具。二、创建型模式管“对象怎么创建”创建型模式的核心作用简化对象创建流程、控制对象创建数量、隐藏对象创建的复杂逻辑让我们不用关注“怎么new对象”只关注“对象怎么用”。Java中最常用的有4种。1. 单例模式Singleton【必用必考】核心需求确保一个类在整个程序运行期间只有一个实例对象比如数据库连接池、日志工具类多实例会造成资源浪费或数据混乱。关键注意点线程安全、防止反射破坏、防止序列化破坏。Java实战代码推荐双重检查锁式兼顾效率和安全public class Singleton { // volatile防止指令重排保证多线程下可见性 private static volatile Singleton instance; // 私有构造方法禁止外部new private Singleton() {} // 双重检查锁第一次检查避免频繁加锁第二次检查防止多线程并发创建 public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }应用场景Spring中的Bean默认是单例非懒加载、Redis客户端连接池、日志工具类Logback的LoggerFactory。2. 工厂模式Factory【必用】工厂模式分3种简单工厂、工厂方法、抽象工厂Java开发中最常用“简单工厂”和“工厂方法”核心是“用工厂代替new统一管理对象创建”。简单工厂模式核心一个工厂造所有对象场景比如我们有“用户登录”“管理员登录”两种登录方式用工厂统一创建登录对象不用在业务代码中频繁new。// 登录接口 public interface Login { boolean login(String username, String password); } // 用户登录实现 public class UserLogin implements Login { Override public boolean login(String username, String password) { // 模拟用户登录逻辑 return user.equals(username) 123456.equals(password); } } // 管理员登录实现 public class AdminLogin implements Login { Override public boolean login(String username, String password) { // 模拟管理员登录逻辑 return admin.equals(username) admin123.equals(password); } } // 简单工厂统一创建登录对象 public class LoginFactory { public static Login getLogin(String type) { if (user.equals(type)) { return new UserLogin(); } else if (admin.equals(type)) { return new AdminLogin(); } throw new IllegalArgumentException(无效的登录类型); } } // 业务使用不用new直接找工厂要 public class Test { public static void main(String[] args) { Login userLogin LoginFactory.getLogin(user); userLogin.login(user, 123456); } }工厂方法模式核心一个工厂造一种对象解耦更彻底场景如果后续要加“第三方登录微信、QQ”简单工厂会不断修改if-else工厂方法则通过“新增工厂类”实现扩展符合“开闭原则”对扩展开放对修改关闭。应用场景Spring的BeanFactory创建Bean的工厂、MyBatis的SqlSessionFactory创建SqlSession。3. 建造者模式Builder【常用】核心需求当一个类的属性很多比如实体类有10个字段创建对象时参数太多容易出错且代码可读性差——建造者模式可以“分步构建对象”让创建过程更清晰。Java实战代码Lombok的Builder注解底层就是这个逻辑public class User { private String username; private String password; private Integer age; private String phone; // 私有构造方法只能通过建造者创建 private User(Builder builder) { this.username builder.username; this.password builder.password; this.age builder.age; this.phone builder.phone; } // 建造者内部类 public static class Builder { private String username; private String password; private Integer age; private String phone; // 链式调用每个方法返回自身 public Builder username(String username) { this.username username; return this; } public Builder password(String password) { this.password password; return this; } public Builder age(Integer age) { this.age age; return this; } public Builder phone(String phone) { this.phone phone; return this; } // 最终构建对象 public User build() { return new User(this); } } } // 业务使用链式调用清晰易懂 public class Test { public static void main(String[] args) { User user new User.Builder() .username(zhangsan) .password(123456) .age(20) .phone(13800138000) .build(); } }应用场景实体类创建尤其是字段多的情况、MyBatis的QueryWrapper链式构建查询条件、OkHttp的请求构建。4. 原型模式Prototype【场景化常用】核心需求当一个对象创建成本很高比如需要查询数据库、调用接口获取数据此时如果需要多个相同/相似的对象直接new会重复消耗资源——原型模式通过“复制对象”浅拷贝/深拷贝减少资源消耗。关键注意点Java中实现Cloneable接口重写clone()方法浅拷贝只复制基本类型深拷贝需要复制引用类型比如List、对象。应用场景Spring的Bean的作用域prototype原型模式每次获取都是新实例但创建逻辑复用、批量创建相同结构的对象比如批量生成用户测试数据。三、结构型模式管“类/对象怎么组合”结构型模式的核心作用将类或对象组合成更灵活、更可扩展的结构解决“如何组装现有模块”的问题核心是“复用现有代码”。Java中最常用的有4种。1. 代理模式Proxy【必用必考】核心需求给一个对象提供“代理对象”通过代理对象控制对原对象的访问——可以在原对象的方法执行前后添加额外逻辑比如日志、权限校验、事务控制这也是AOP的核心原理。Java中代理分3种静态代理、动态代理JDK代理、CGLIB代理最常用的是动态代理不用手动写代理类自动生成。JDK动态代理实战基于接口Spring AOP默认使用// 目标接口被代理的接口 public interface UserService { void addUser(String username); } // 目标实现类原对象 public class UserServiceImpl implements UserService { Override public void addUser(String username) { System.out.println(新增用户 username); } } // 动态代理处理器添加额外逻辑 public class MyInvocationHandler implements InvocationHandler { // 目标对象被代理的对象 private Object target; public MyInvocationHandler(Object target) { this.target target; } // 代理方法每次调用目标方法都会执行这里 Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 方法执行前添加日志 System.out.println(日志开始执行 method.getName() 方法); // 执行目标方法 Object result method.invoke(target, args); // 方法执行后添加事务提交 System.out.println(日志 method.getName() 方法执行完成); return result; } } // 测试动态代理 public class Test { public static void main(String[] args) { // 1. 创建目标对象 UserService userService new UserServiceImpl(); // 2. 创建代理处理器 MyInvocationHandler handler new MyInvocationHandler(userService); // 3. 生成代理对象JDK自动生成 UserService proxy (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler ); // 4. 调用代理对象的方法会触发invoke方法 proxy.addUser(zhangsan); } }应用场景Spring AOP日志、事务、权限控制、MyBatis的Mapper代理不用写实现类自动生成代理对象、Redis缓存代理。2. 装饰器模式Decorator【常用】核心需求给一个对象动态添加额外功能且不修改原对象的代码——和代理模式的区别装饰器模式侧重“增强功能”代理模式侧重“控制访问”。Java实战代码比如给IO流添加缓冲功能// 核心接口被装饰的对象接口 public interface Drink { String getName(); // 饮品名称 double getPrice(); // 饮品价格 } // 基础实现类被装饰的原对象 public class Coffee implements Drink { Override public String getName() { return 纯咖啡; } Override public double getPrice() { return 10.0; } } // 装饰器抽象类实现Drink接口持有被装饰对象 public abstract class DrinkDecorator implements Drink { protected Drink drink; public DrinkDecorator(Drink drink) { this.drink drink; } Override public String getName() { return drink.getName(); } Override public double getPrice() { return drink.getPrice(); } } // 具体装饰器1加牛奶 public class MilkDecorator extends DrinkDecorator { public MilkDecorator(Drink drink) { super(drink); } Override public String getName() { return drink.getName() 牛奶; } Override public double getPrice() { return drink.getPrice() 3.0; // 加牛奶加3元 } } // 具体装饰器2加糖 public class SugarDecorator extends DrinkDecorator { public SugarDecorator(Drink drink) { super(drink); } Override public String getName() { return drink.getName() 糖; } Override public double getPrice() { return drink.getPrice() 1.0; // 加糖加1元 } } // 测试装饰器 public class Test { public static void main(String[] args) { // 基础纯咖啡 Drink coffee new Coffee(); System.out.println(coffee.getName() coffee.getPrice() 元); // 加牛奶 Drink milkCoffee new MilkDecorator(coffee); System.out.println(milkCoffee.getName() milkCoffee.getPrice() 元); // 加牛奶糖嵌套装饰 Drink milkSugarCoffee new SugarDecorator(milkCoffee); System.out.println(milkSugarCoffee.getName() milkSugarCoffee.getPrice() 元); } }应用场景Java IO流BufferedInputStream装饰FileInputStream添加缓冲功能、Spring的BeanWrapper装饰Bean添加属性编辑功能。3. 适配器模式Adapter【场景化常用】核心需求解决“两个接口不兼容无法直接调用”的问题——通过适配器将一个接口转换成另一个接口让原本不能一起工作的类可以一起工作。例子比如我们有一个老系统的“支付接口”返回String类型结果新系统需要“支付接口”返回Boolean类型结果此时就可以用适配器转换。应用场景老系统改造兼容新接口、第三方接口适配比如不同支付平台的接口统一适配成自己系统的接口、Java的Arrays.asList()将数组适配成List。4. 组合模式Composite【场景化常用】核心需求将“单个对象”和“对象集合”统一对待形成树形结构——比如文件夹和文件文件夹里可以放文件也可以放子文件夹我们可以统一操作“文件夹”和“文件”。应用场景树形结构展示比如菜单树、部门树、文件系统管理、MyBatis的SQL节点比如ifwhere标签既是单个节点也能组合成复杂SQL。四、行为型模式管“对象之间怎么通信、协作”行为型模式的核心作用解决“对象之间的交互逻辑”让对象之间的通信更灵活、更解耦避免出现“一个对象依赖多个对象”的情况。Java中最常用的有3种。1. 策略模式Strategy【必用】核心需求定义一系列算法将每个算法封装起来并且可以互相替换——比如一个支付功能有“微信支付”“支付宝支付”“银行卡支付”算法不同但调用方式一致方便切换。Java实战代码// 策略接口定义算法的统一方法 public interface PayStrategy { void pay(double money); // 支付方法 } // 具体策略1微信支付 public class WeChatPay implements PayStrategy { Override public void pay(double money) { System.out.println(微信支付 money 元); } } // 具体策略2支付宝支付 public class Alipay implements PayStrategy { Override public void pay(double money) { System.out.println(支付宝支付 money 元); } } // 策略上下文统一调用策略切换策略 public class PayContext { private PayStrategy payStrategy; // 设置策略可以动态切换 public void setPayStrategy(PayStrategy payStrategy) { this.payStrategy payStrategy; } // 统一调用支付方法 public void pay(double money) { payStrategy.pay(money); } } // 测试策略模式 public class Test { public static void main(String[] args) { PayContext payContext new PayContext(); // 切换微信支付 payContext.setPayStrategy(new WeChatPay()); payContext.pay(100.0); // 切换支付宝支付 payContext.setPayStrategy(new Alipay()); payContext.pay(200.0); } }应用场景支付方式切换、排序算法切换比如ArrayList的sort方法可传入不同的Comparator策略、Spring的BeanPostProcessor不同的后置处理器策略。2. 观察者模式Observer【常用】核心需求定义“一对多”的依赖关系当一个对象被观察者的状态发生变化时所有依赖它的对象观察者都会自动收到通知并更新——比如“发布-订阅”模式。Java实战代码Java自带Observer和Observable接口// 被观察者主题比如公众号 public class WeChatPublic extends Observable { private String message; public void setMessage(String message) { this.message message; setChanged(); // 标记状态已变化 notifyObservers(message); // 通知所有观察者 } } // 观察者1用户A public class UserA implements Observer { Override public void update(Observable o, Object arg) { System.out.println(用户A收到公众号消息 arg); } } // 观察者2用户B public class UserB implements Observer { Override public void update(Observable o, Object arg) { System.out.println(用户B收到公众号消息 arg); } } // 测试观察者模式 public class Test { public static void main(String[] args) { // 创建被观察者公众号 WeChatPublic publicAccount new WeChatPublic(); // 注册观察者用户关注公众号 publicAccount.addObserver(new UserA()); publicAccount.addObserver(new UserB()); // 发布消息被观察者状态变化 publicAccount.setMessage(Java设计模式详解更新啦); } }应用场景Spring的事件驱动模型ApplicationEvent和ApplicationListener、消息队列Kafka、RabbitMQ的发布-订阅模式、GUI界面的事件监听比如按钮点击事件。3. 模板方法模式Template Method【常用】核心需求定义一个方法的“骨架”将方法中的某些步骤延迟到子类中实现——比如一个“登录流程”步骤是“验证账号密码→处理登录→记录日志”其中“验证账号密码”和“处理登录”因用户类型不同而不同“记录日志”是通用步骤就可以用模板方法。Java实战代码// 模板抽象类定义登录流程骨架 public abstract class LoginTemplate { // 模板方法定义登录流程不可修改 public final void login(String username, String password) { // 步骤1验证账号密码子类实现 boolean check checkUsernameAndPassword(username, password); if (!check) { System.out.println(账号密码错误); return; } // 步骤2处理登录子类实现 handleLogin(username); // 步骤3记录日志通用步骤父类实现 logLogin(username); } // 抽象方法验证账号密码子类实现 protected abstract boolean checkUsernameAndPassword(String username, String password); // 抽象方法处理登录子类实现 protected abstract void handleLogin(String username); // 具体方法记录日志通用步骤 private void logLogin(String username) { System.out.println(username 登录成功时间 new Date()); } } // 子类1用户登录 public class UserLoginTemplate extends LoginTemplate { Override protected boolean checkUsernameAndPassword(String username, String password) { return user.equals(username) 123456.equals(password); } Override protected void handleLogin(String username) { System.out.println(用户 username 登录成功跳转到用户首页); } } // 子类2管理员登录 public class AdminLoginTemplate extends LoginTemplate { Override protected boolean checkUsernameAndPassword(String username, String password) { return admin.equals(username) admin123.equals(password); } Override protected void handleLogin(String username) { System.out.println(管理员 username 登录成功跳转到管理员后台); } } // 测试模板方法 public class Test { public static void main(String[] args) { // 用户登录 LoginTemplate userLogin new UserLoginTemplate(); userLogin.login(user, 123456); // 管理员登录 LoginTemplate adminLogin new AdminLoginTemplate(); adminLogin.login(admin, admin123); } }应用场景Spring的JdbcTemplate定义JDBC操作骨架子类实现具体的SQL执行、JUnit的TestCase定义测试流程骨架、各种框架的“模板类”统一流程灵活扩展。五、最后设计模式的使用误区很多新手学完设计模式会陷入“为了用设计模式而用设计模式”的误区记住3个原则避免踩坑不要过度设计如果业务简单比如一个简单的工具类不用强行用设计模式否则会增加代码复杂度。优先满足业务设计模式是工具不是目的先保证业务能正常运行再考虑用设计模式优化。理解本质而非死记代码每个设计模式的核心是“解决什么问题”比如单例是“控制实例数量”代理是“增强控制访问”理解本质才能灵活运用。对于Java程序员来说掌握上面这11种设计模式足以应对日常开发和面试。后续可以结合Spring、MyBatis等框架的源码看看框架是如何运用这些设计模式的加深理解