一、设计模式总纲(底层思想 + 核心本质 + 六大设计原则 + 三大分类详解)
1. 底层核心思想(面试核心开篇)
设计模式 是面向对象软件开发中,经过无数项目验证、通用、可复用的经典设计解决方案,专门解决代码耦合高、扩展性差、复用率低、逻辑混乱、架构臃肿等工程痛点。
核心底层思维(四句万能核心):
-
封装变化:将项目中频繁变动的代码、规则、状态独立封装,稳定代码不动,变动代码隔离
-
复用优先:杜绝重复造轮子,通过继承、组合、多态实现代码极致复用
-
解耦协作:拆分臃肿逻辑,降低类与类、模块与模块的依赖关系
-
面向扩展:所有设计均为后续迭代预留扩展空间,规避重构风险
终极设计理念 :多用组合、少用继承;依赖抽象、不依赖细节(所有23种模式的通用底层准则)
2 面向对象四大核心特性(模式设计底层基石|必懂)
核心说明 :所有23种设计模式,100%基于面向对象四大特性衍生。没有封装、继承、多态、抽象,就没有设计模式的解耦、复用、扩展能力。本节补齐完整标准定义、模式支撑原理、实战落地价值。
2.1.封装 Encapsulation(隔离细节、单一职责根基)
标准定义 :将对象的属性、内部行为、核心实现细节私有化隐藏,仅对外暴露可控的公共访问方法,屏蔽内部复杂逻辑,保证数据安全与职责收敛。
支撑设计模式的底层原理 :封装是所有模式的基础前提。模式能实现解耦、分层、职责单一,本质依靠封装隐藏内部实现,外部只调用统一入口。
模式落地场景:
-
单例模式:私有化构造方法,禁止外部随意new对象,全局唯一实例可控
-
门面模式:隐藏底层多子系统复杂逻辑,对外统一极简入口
-
代理/装饰器模式:屏蔽原对象细节,只对外提供增强后的统一能力
2.2 继承 Inheritance(代码复用、骨架抽象)
标准定义 :子类复用父类的非私有属性与方法,在父类通用能力基础上,实现自定义扩展,实现代码复用、骨架统一。
支撑设计模式的底层原理 :继承用于搭建通用模板骨架,固化公共逻辑,让子类专注实现差异化逻辑。
核心约束(模式设计铁律) :少用继承、多用组合。继承属于静态绑定、耦合度高、层级僵硬;组合灵活、可动态替换,是现代模式主流方案。
模式落地场景:
-
模板方法模式:父类继承固化通用流程,子类重写差异化步骤
-
适配器、抽象工厂:通过继承统一顶层规范,约束子类行为
2.3 抽象 Abstraction(规范统一、预留扩展)
标准定义 :抽取一类对象的通用共性能力,忽略细节差异,通过抽象类/接口定义统一行为规范,预留扩展口子,约束所有实现类。
支撑设计模式的底层原理 :抽象是解耦、分层、统一架构的核心。高层模块依赖抽象,不依赖具体实现,彻底满足依赖倒置原则。
模式落地场景:
-
所有行为型、创建型模式顶层接口抽象
-
责任链统一处理器抽象、观察者统一事件监听抽象
-
适配层、通用工具层统一抽象规范,抹平多实现差异
2.4 多态 Polymorphism(动态扩展、开闭原则核心)
标准定义 :父类引用指向子类对象,程序运行时动态绑定子类实现,同一接口、不同实现、动态执行 。分为编译多态(重载)、运行多态(重写),模式核心依赖运行时多态。
支撑设计模式的底层原理 :多态是开闭原则的技术核心,也是所有扩展型模式的灵魂。依靠多态,可实现不修改父类代码,新增子类扩展能力。
模式落地场景(高频核心):
-
策略模式:统一策略接口,不同算法子类动态切换
-
工厂模式:父工厂统一规范,子类工厂生产不同产品
-
状态模式:统一状态接口,不同状态子类驱动不同业务行为
-
Spring Bean 注入:依赖父类接口,动态注入任意子类实现
四大特性与设计模式终极对应总结(面试速记)
-
封装 → 解决:代码混乱、细节暴露 → 支撑所有模式的职责单一
-
继承 → 解决:代码重复、流程不统一 → 支撑模板、抽象骨架复用
-
多态 → 解决:固定代码无法扩展、分支臃肿 → 支撑策略、工厂、状态等核心扩展模式
-
抽象 → 解决:层级耦合、实现不统一 → 支撑依赖倒置、架构分层解耦
核心结论 :六大设计原则是思想准则 ,四大特性是技术底座 ,23种设计模式是落地产物,三者层层递进、完全闭环。
3. 六大设计原则(顶层约束|完整版|通俗解读+工程落地+面试标准答案)
总纲定义 :六大设计原则是面向对象编程的官方设计规范 ,是评判代码质量(高内聚、低耦合、易扩展、易维护)的唯一标准。 所有23种设计模式,本质都是对六大原则的具体落地实现 。 核心目的:解耦、复用、可控、易扩展、少出Bug。
3.1 单一职责原则(SRP)------ 高内聚核心
官方定义 :一个类、一个方法、一个接口,应当仅有一个引起它变化的原因,只负责一项核心职责。
通俗解读:一个类只干一件事,一个方法只做一个逻辑,不写万能类、不写万能方法。
解决痛点:杜绝臃肿大方法、万能类、逻辑混杂,改一处崩全局。
工程落地规范:
-
工具类:单一功能拆分(日期工具、字符串工具、文件工具分开)
-
业务Service:按领域拆分(订单、用户、支付独立Service)
-
方法设计:一个方法只完成一个动作,不混杂查询、修改、删除逻辑
反面案例:一个 UserService 同时处理用户注册、订单生成、支付扣款、日志记录。
面试标准答案:单一职责原则保证类和方法职责高度内聚,代码清晰、修改风险极低,是所有代码整洁度的基础保障。
3.2 开闭原则(OCP)------ 架构核心原则【最重要】
官方定义 :软件实体(类、方法、模块)应当对扩展开放,对修改关闭。
通俗解读 :新增需求、新增功能,尽量不改动原有成熟代码,只通过新增代码实现扩展。
解决痛点:迭代改旧代码、引发回归Bug、线上故障频发。
工程落地手段:
-
通过接口、抽象类预留扩展口子
-
使用策略、工厂、观察者等模式替代硬编码if-else
-
业务差异化逻辑通过新增子类/实现类完成
落地场景:支付渠道扩展、优惠算法扩展、日志组件替换、多数据源适配
面试标准答案:开闭原则是架构设计的最高准则,通过扩展代替修改,保证原有稳定代码不受迭代影响,极大提升系统稳定性与可扩展性。
3.3 里氏替换原则(LSP)------ 多态基石
官方定义 :所有使用父类的地方,必须可以完全使用子类替换,替换后程序逻辑、功能、行为无任何变更。
通俗解读:子类是父类的纯粹拓展,不能重写毁掉父类逻辑,不能改变原有行为。
解决痛点:子类重写父类核心方法导致多态失效、程序逻辑错乱。
核心约束规范:
-
子类可以拓展父类方法,禁止篡改父类原有核心逻辑
-
子类前置条件不能严于父类
-
子类返回结果、异常范围不能超出父类定义
工程落地:Spring多态注入、接口多实现、模板方法模式、工厂模式
面试标准答案:里氏替换原则保证继承体系的稳定性,是多态实现的底层前提,确保系统替换实现类时无缝兼容、无逻辑异常。
3.4 依赖倒置原则(DIP)------ 解耦核心
官方定义:高层模块不依赖低层模块,二者都依赖抽象;抽象不依赖细节,细节依赖抽象。
通俗解读 :面向接口编程,不面向实现类编程。
解决痛点:层级耦合严重、底层改动影响上层、代码僵硬无法替换实现。
工程落地手段:
-
Spring IoC核心思想:依赖注入、面向接口注入
-
所有业务调用上层依赖接口,不new具体实现类
-
随时替换接口实现类,无需改动上层业务代码
落地场景:替换支付实现、切换数据源、切换缓存组件、策略模式动态替换算法
面试标准答案 :依赖倒置原则通过依赖抽象彻底解耦上下级模块,消除硬编码依赖,让系统实现插拔式扩展,是Spring框架核心设计思想。
3.5 接口隔离原则(ISP)------ 精细化解耦
官方定义:客户端不应当依赖它不需要的接口,应当提供多个细粒度专用接口,拒绝臃肿万能大接口。
通俗解读:接口要小而精,不要大而全;不用的方法不要强行实现。
解决痛点:大接口冗余方法多、实现类被迫空实现、接口职责混乱。
工程落地规范:
-
拆分万能接口:查询接口、新增接口、修改接口、删除接口分离
-
不同业务模块使用专属独立接口
-
杜绝一个接口承载所有业务能力
反面案例:一个 UserAllService 包含用户、订单、支付、日志所有方法。
面试标准答案:接口隔离原则细化接口职责,避免无效依赖与空实现,让接口更纯粹、系统耦合度更低。
3.6 迪米特法则(LoD|最少知道原则)------ 低耦合终极准则
官方定义:一个对象应当对其他对象保持最少了解,只与直接朋友通信,不与陌生对象直接交互。
通俗解读 :越少知道越好,类与类之间尽量不直接嵌套依赖,不深挖对方内部细节。
直接朋友定义:参数、成员变量、返回值、局部对象。
解决痛点:多级依赖、代码牵一发而动全身、架构错综复杂。
工程落地手段:
-
通过中间层(门面、中介者、Service)转发调用
-
禁止链式调用多级对象属性(a.getB().getC().getD())
-
封装内部细节,对外只暴露极简入口
落地模式:门面模式、中介者模式、代理模式均为迪米特法则落地
面试标准答案:迪米特法则最大限度降低类之间的依赖关系,减少代码耦合,让系统架构更稳定、迭代维护成本更低。
3.7 六大原则终极对照表(面试必背|一秒区分)
| 设计原则 | 核心关键词 | 解决问题 | 核心落地模式 |
|---|---|---|---|
| 单一职责 SRP | 一个类一件事 | 代码臃肿、职责混乱 | 所有模式基础 |
| 开闭原则 OCP | 扩展开放、修改关闭 | 迭代改代码、出Bug | 策略、工厂、观察者 |
| 里氏替换 LSP | 子类可完全替换父类 | 多态失效、继承混乱 | 模板方法、所有多态场景 |
| 依赖倒置 DIP | 依赖抽象、不依赖细节 | 层级耦合、无法扩展 | Spring IoC、所有接口设计 |
| 接口隔离 ISP | 细粒度小接口 | 接口臃肿、空实现 | 系统接口分层设计 |
| 迪米特 LoD | 最少知道、低耦合 | 依赖复杂、牵一发而动全身 | 门面、中介者、代理 |
3.8 六大原则极简背诵口诀(面试秒答)
单职各司其职,开闭扩展不改; 里氏子类兼容,倒置依赖抽象; 接口精细隔离,迪米最少知晓。
3.9 面试万能总结(面试官最爱听)
六大设计原则是面向对象设计的顶层规范:以单一职责 保证代码内聚,以开闭原则 支撑架构扩展,以里氏替换 保障继承多态稳定,以依赖倒置 实现层级解耦,以接口隔离 精简依赖,以迪米特法则降低耦合,所有设计模式与高质量业务代码,均围绕六大原则落地实现。
4. 23种模式三大分类(超全完整版|核心定位+本质区别+代码特征+面试必背)
前置核心结论 :23种设计模式并非杂乱无章,根据代码作用维度、解决的工程痛点、耦合对象层级 ,严格划分为三大类。 一句话层级闭环:创建型管"对象怎么造"、结构型管"对象怎么拼"、行为型管"对象怎么聊"。 所有模式设计,均严格遵循:四大特性为底座、六大原则为约束。
三大分类核心差异总览(面试秒杀区分)
-
创建型模式 :解决 对象创建耦合 → 控制实例生成规则、统一对象创建方式
-
结构型模式 :解决 对象结构僵硬 → 通过组合/包装适配,灵活组装系统架构
-
行为型模式 :解决 对象交互混乱 → 规范多对象通信、流程、状态、算法调度
4.1 创建型模式(共5种)|管控对象实例化
完整核心定位: 屏蔽、隔离、封装对象的创建过程,脱离硬编码 new 耦合,统一实例创建规则,解决「对象创建混乱、资源浪费、多实例不统一、复杂对象构建繁琐」的核心问题。
本质底层思想 :将"对象创建"与"对象使用"彻底解耦。使用者只管用,不用管怎么造。
核心解决痛点:
-
new 硬编码导致代码耦合极高,替换实现需要大面积改代码
-
复杂对象参数多、构建步骤乱,代码可读性极差
-
重复创建相同对象,造成内存资源浪费
-
无法统一全局实例规则(全局唯一、统一工厂产出)
代码核心特征: 全部围绕「实例获取、对象构建、对象克隆」展开,不关注对象功能、不修改对象结构、不处理对象交互。
5种模式精准分工:
-
单例:控数量 → 全局唯一实例,资源独享
-
工厂方法:控单一产品 → 单维度产品扩展,一厂一品
-
抽象工厂:控产品族 → 多维度成套产品,保证组件配套兼容
-
建造者:控构建流程 → 复杂对象分步组装,灵活选配参数
-
原型:控创建性能 → 克隆复用对象,规避重复new开销
面试标准答案:创建型模式通过封装对象实例化过程,解耦对象创建与业务使用,统一实例创建规则,解决了硬编码耦合、对象资源浪费、复杂对象构建混乱问题,适配所有场景的对象初始化与资源管控。
4.2 结构型模式(共7种)|组装系统架构
完整核心定位 : 基于组合、包装、适配、复用、分层思想,对已有类和对象进行结构重组、功能包装、接口兼容、内存优化,在不改动原有对象核心源码的前提下,搭建灵活、低耦合、可扩展的系统架构。
本质底层思想 :多用组合、少用继承,通过对象拼装改造架构,实现功能增强、结构优化、层级简化。
核心解决痛点:
-
原有接口不兼容、新旧系统无法对接
-
继承导致类爆炸、架构层级僵硬无法扩展
-
底层子系统复杂,上层调用繁琐
-
大量细粒度对象重复创建,内存占用过高
-
树形结构对象无法统一遍历处理
代码核心特征 : 不创建新对象、不修改业务流程,只对已有对象进行包装、适配、组合、复用、隐藏,属于「结构性优化」。
7种模式精准分工(四大分组):
-
接口适配组(兼容异构):适配器(事后兼容)、桥接(事前解耦防类爆炸)
-
功能包装组(增强管控):装饰器(叠加功能)、代理(管控访问)
-
架构简化组(解耦层级):外观门面(统一入口,简化调用)
-
性能结构组(优化架构):享元(内存复用)、组合(树形结构统一处理)
面试标准答案:结构型模式以组合复用为核心,通过适配、包装、分层、复用等方式改造对象结构,无需修改原有业务代码,即可实现系统架构兼容、增强、简化、优化,是搭建高灵活架构的核心模式。
4.3 行为型模式(共11种)|规范对象协作
完整核心定位 : 专注于类与对象之间的通信方式、业务流程流转、算法调度、状态切换、事件驱动、请求处理,梳理多对象协作逻辑,消灭臃肿if-else、流程混乱、耦合交互问题,标准化业务行为。
本质底层思想 :封装变化的业务行为、拆分复杂交互逻辑、解耦对象通信依赖。
核心解决痛点:
-
业务分支过多,if-else/switch 代码臃肿难以维护
-
多对象双向依赖、通信混乱、耦合严重
-
业务流程固定、无法动态扩展、无法灵活调整
-
状态流转混乱、算法切换僵硬、事件通知耦合
-
请求无法回溯、无法排队、无法统一遍历
代码核心特征 : 不关注对象创建、不修改对象结构,只关注对象运行时的行为、交互、流程、规则,属于「业务行为优化」。
11种模式精准分工(八大业务场景分组,面试最好记):
-
流程管控:模板方法(固定骨架)、责任链(动态链式处理)
-
算法调度:策略(平等算法切换)
-
状态流转:状态(有序状态自动切换)
-
事件驱动:观察者(一对多发布订阅)
-
请求封装:命令(请求可回溯)、迭代器(统一遍历)
-
多解耦通信:中介者(多对多通信解耦)
-
状态快照:备忘录(状态备份回滚)
-
拓展解析:访问者(操作与结构分离)、解释器(自定义语法解析)
面试标准答案:行为型模式聚焦对象间的协作与业务行为,通过封装算法、流程、状态、事件、请求,彻底消灭分支冗余代码,解耦多对象交互依赖,标准化业务流程与规则,是业务代码重构的核心方案。
4.4 三大分类终极横向对比(面试高频口述)
| 模式分类 | 核心关注点 | 解决核心问题 | 改造维度 | 模式数量 |
|---|---|---|---|---|
| 创建型 | 对象实例怎么创建 | 创建耦合、资源浪费、构建混乱 | 对象生命周期起始 | 5 |
| 结构型 | 对象结构怎么组合 | 架构僵硬、接口不兼容、层级复杂 | 对象静态结构 | 7 |
| 行为型 | 对象行为怎么协作 | 流程混乱、分支臃肿、交互耦合 | 对象动态行为 | 11 |
4.5 三大分类终极面试万能总结(满分话术)
23种设计模式分为创建型、结构型、行为型三大类,层层递进覆盖软件开发全流程。创建型 负责解耦对象创建,管控实例生成;结构型 负责优化对象组合,搭建灵活架构;行为型负责规范对象协作,梳理业务流程。三者基于面向对象四大特性、遵循六大设计原则,分别从对象诞生、对象结构、对象行为三个维度,解决代码耦合高、扩展性差、复用率低、维护成本高的工程痛点。
23种设计模式严格分为三大类,分类核心依据:解决的工程问题不同、代码作用维度不同
5. 总纲面试万能总结(口述标准答案)
设计模式基于面向对象四大特性搭建,以六大设计原则为顶层约束,分为创建型、结构型、行为型三大类。分别负责管控对象创建、组装系统结构、规范对象交互,核心目的是解耦、复用、可扩展、易维护,是Java框架底层源码的核心设计思想,也是业务代码重构的最优实践方案。
二、第一大类:创建型模式(5种|完整版|面试满分体系)
大类总纲回顾 :创建型模式核心是解耦对象创建与对象使用 ,屏蔽 new 硬编码耦合,统一实例创建规则,解决对象创建混乱、资源浪费、复杂对象构建不可控等问题。 适用场景 :所有对象初始化、资源创建、实例管控场景。 设计原则落地 :高度贴合 开闭原则、单一职责、依赖倒置。
1. 单例模式(Singleton)------ 全局唯一实例
1.1 核心定义与底层思想
官方定义 :保证一个类在整个系统生命周期中仅有一个实例,并提供一个全局统一的访问入口。
核心解决痛点:频繁创建销毁工具类、资源类导致内存浪费、实例不统一、全局状态混乱。
核心实现方式(5种,面试必背)
-
饿汉式:类加载直接初始化,线程安全,简单高效,缺点是闲置浪费内存
-
懒汉式:用时才创建,懒加载,原生线程不安全
-
DCL双重检查锁:懒加载+线程安全+高性能,工程最常用,需volatile禁止指令重排
-
静态内部类:无锁、懒加载、线程安全,手写最优方案
-
枚举单例 :《Effective Java》推荐,杜绝反射、序列化破坏单例,绝对安全
核心优缺点
-
✅ 优点:全局资源统一管控、减少对象创建销毁开销、节约内存、访问统一
-
❌ 缺点:无状态适合,有状态会出现线程竞争;无法继承扩展;长期常驻内存
工程落地场景:Spring默认单例Bean、系统配置类、连接池、线程池、Redis客户端、工具类、Runtime运行实例
面试易错点 :必须回答 volatile作用(禁止指令重排)、反射破坏问题、枚举单例安全性
面试标准答案:单例模式通过私有化构造方法禁止外部实例化,保证全局唯一实例,统一资源访问入口,减少对象创建开销,适合全局无状态资源管控,是工程中最常用的资源优化创建模式。
1.2 单例模式 五种实战代码(可直接手撕|面试满分代码)
核心编码铁律:私有化构造方法 + 私有静态实例 + 公共静态获取方法
① 饿汉式(线程安全|简单常用|类加载初始化)
java
/**
* 饿汉式单例
* 优点:类加载时初始化,天然线程安全、执行效率高
* 缺点:无论是否使用,都会占用内存,存在资源浪费
* 适用:单例对象占用内存小、频繁使用场景
*/
public class SingletonHungry {
// 1. 私有静态实例:类加载直接创建
private static final SingletonHungry INSTANCE = new SingletonHungry();
// 2. 私有化构造方法:禁止外部new
private SingletonHungry(){}
// 3. 公共统一访问入口
public static SingletonHungry getInstance(){
return INSTANCE;
}
}
② 普通懒汉式(线程不安全|面试反例)
java
/**
* 懒汉式单例(线程不安全)
* 特点:延迟加载,用时才创建
* 致命问题:多线程下会创建多个实例,并发场景失效
*/
public class SingletonLazy {
// 私有静态实例,初始为空
private static SingletonLazy instance;
// 私有化构造
private SingletonLazy(){}
public static SingletonLazy getInstance(){
// 多线程同时进入判断,会重复创建对象
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
③ DCL双重检查锁(工程常用|高性能线程安全)
java
/**
* DCL双重检查锁单例(推荐工程使用)
* 核心:懒加载 + 线程安全 + 高性能
* volatile:禁止指令重排,防止半初始化对象溢出
*/
public class SingletonDCL {
// volatile 解决指令重排问题
private static volatile SingletonDCL instance;
private SingletonDCL(){}
public static SingletonDCL getInstance(){
// 第一次校验:避免每次加锁,提升性能
if (instance == null) {
// 加锁保证同一时间只有一个线程创建对象
synchronized (SingletonDCL.class) {
// 第二次校验:防止多线程阻塞重复创建
if (instance == null) {
instance = new SingletonDCL();
}
}
}
return instance;
}
}
面试必问:volatile作用:new对象分为分配内存、初始化、赋值三步,volatile禁止指令重排,避免线程获取到未初始化完成的半实例对象。
④ 静态内部类单例(手写最优|无锁高效|线程安全)
java
/**
* 静态内部类单例(面试手写最优方案)
* 原理:外部类加载不加载内部类,调用方法才加载,实现懒加载
* 天然线程安全:静态内部类只会加载一次
* 无锁、高性能、代码简洁
*/
public class SingletonInner {
// 私有化构造
private SingletonInner(){}
// 静态内部类:持有外部类实例
private static class SingletonHolder{
private static final SingletonInner INSTANCE = new SingletonInner();
}
// 统一访问入口
public static SingletonInner getInstance(){
return SingletonHolder.INSTANCE;
}
}
⑤ 枚举单例(绝对安全|杜绝反射/序列化破坏|终极方案)
java
/**
* 枚举单例(《Effective Java》官方推荐)
* 天然单例、线程安全、杜绝反射、序列化破坏
* 唯一缺点:不支持懒加载
*/
public enum SingletonEnum {
// 唯一实例
INSTANCE;
// 自定义业务方法
public void doSomething(){
System.out.println("枚举单例执行业务逻辑");
}
}
1.3 单例模式 面试高频难题(必背)
问题1:如何破坏单例?如何解决?
-
破坏方式:反射暴力调用私有化构造方法、序列化反序列化重建对象
-
唯一彻底解决方案 :枚举单例,JDK底层禁止反射创建枚举实例
问题2:五种单例优劣终极对比
-
饿汉式:简单安全、不懒加载、浪费内存
-
普通懒汉:懒加载、线程不安全、废弃写法
-
DCL:懒加载、安全、高性能、需volatile
-
静态内部类:无锁高效、懒加载、手写首选
-
枚举:绝对安全、防破坏、不支持懒加载、框架首选
问题3:Spring单例与手写单例区别?
Spring单例是IoC容器级单例 :仅在同一个Spring容器、同一个Bean作用域下保证唯一,是业务容器单例 ;手写Java单例是JVM全局级单例 ,整个JVM内仅一个实例。 核心区别:Spring单例不保证线程安全,多线程并发操作共享变量会出现线程安全问题;传统单例模式可通过代码保障线程安全。同时Spring单例支持Scope动态切换,手写单例实例全局固定。
1.4 单例模式 底层核心原理(面试深挖必问)
① 为什么普通懒汉式线程不安全?
多线程并发场景下,多个线程可同时通过 instance == null 判断,依次执行创建对象逻辑,最终生成多个实例,彻底破坏单例特性,并发场景完全失效。
② DCL中volatile核心原理(面试高频)
new 对象底层三步指令(存在重排风险):
-
分配对象内存空间
-
初始化对象成员变量
-
将instance引用指向内存地址
JVM指令重排会将执行顺序变为 1→3→2 ,导致线程拿到未初始化完成的半初始化对象 ,引发空指针、数据异常。 volatile核心作用 :禁止指令重排序、保证内存可见性,严格保证new对象三步顺序执行,彻底杜绝半初始化问题。
③ 静态内部类单例为什么天然线程安全?
JVM静态语法规则:静态内部类只会在首次主动调用时加载一次,且加载过程由JVM底层加锁保证线程安全。外部类加载不会触发内部类加载,实现懒加载;无需手动加锁,兼顾安全、性能、简洁,是手写最优方案。
④ 枚举单例为什么绝对安全?
-
JDK底层禁止通过反射获取枚举构造方法,直接抛出异常,杜绝反射破坏;
-
序列化/反序列化时,枚举会直接返回原有常量实例,不会新建对象;
-
天然线程安全、单例唯一,无任何并发漏洞,是《Effective Java》终极推荐方案。
1.5 单例模式 两大破坏方式 + 终极解决方案(面试压轴题)
1. 反射破坏单例
破坏原理 :通过反射暴力获取私有构造方法,无视私有化权限,手动new新对象,生成多个实例。 普通单例补救方案 :在私有构造方法中增加判空校验,若实例已存在则直接抛异常。 终极方案 :使用枚举单例,从JDK底层杜绝反射创建实例。
java
// 防止反射破坏的构造方法加固
private SingletonDCL(){
if (instance != null){
throw new RuntimeException("禁止重复创建单例实例");
}
}
2. 序列化破坏单例
破坏原理 :对象序列化写入文件/网络后,反序列化会重新创建全新对象,打破全局唯一。 普通单例补救方案 :重写 readResolve() 方法,返回全局已有实例。 终极方案:枚举单例天然支持序列化,无需手动处理。
java
// 防止序列化破坏
private Object readResolve(){
return instance;
}
1.6 五种单例模式全方位终极对比(面试满分对照表)
| 单例写法 | 懒加载 | 线程安全 | 高性能 | 防反射/序列化 | 适用场景 |
|---|---|---|---|---|---|
| 饿汉式 | ❌ | ✅ | ✅ | ❌ | 小内存、高频使用全局资源 |
| 普通懒汉式 | ✅ | ❌ | ✅ | ❌ | 面试反例、废弃不用 |
| DCL双重检查锁 | ✅ | ✅ | ✅ | 需手动加固 | 工程主流、并发业务场景 |
| 静态内部类 | ✅ | ✅ | ✅ | ❌ | 面试手写首选、简洁高效 |
| 枚举单例 | ❌ | ✅ | ✅ | ✅ 绝对安全 | 框架底层、核心全局配置 |
1.7 单例模式 实战踩坑总结(工程落地避坑)
-
坑1:单例类存在成员变量 :单例全局唯一,成员变量为共享资源,多线程并发修改会出现线程安全问题,单例类必须保证无状态
-
坑2:盲目使用饿汉式 :大内存对象使用饿汉式,项目启动常驻内存,造成内存冗余浪费
-
坑3:忽略指令重排:DCL不写volatile,高并发下大概率出现半初始化对象,引发线上诡异Bug
-
坑4:不处理反射/序列化:缓存、序列化场景下,单例被悄悄破坏,实例不唯一
1.8 单例模式 面试满分终极总结(口述万能话术)
单例模式是最常用的创建型模式,核心通过私有化构造方法,保证系统全局仅有一个实例,统一资源访问入口、减少对象创建销毁开销、节约内存。主流实现包含饿汉式、懒汉式、DCL双重检查锁、静态内部类、枚举五种。其中DCL适合工程并发场景,静态内部类简洁高效适合手写,枚举单例安全性最高,可彻底杜绝反射与序列化破坏。单例模式核心适用于无状态全局资源管控,同时需规避共享变量线程安全、指令重排、实例破坏等问题,也是Spring单例Bean、框架底层资源管控的核心设计思想。
2. 工厂方法模式(Factory Method)------ 单一产品扩展工厂
2.1 核心定义与底层思想
官方标准定义:定义一个用于创建对象的抽象工厂接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类完成。
通俗解读 :统一工厂规范,每个具体工厂只负责生产一种产品,新增产品就新增对应工厂,不修改原有代码,完美适配开闭原则。
核心设计思想:剥离硬编码 new 耦合,将「对象创建逻辑」与「业务使用逻辑」彻底解耦,通过多态实现产品的动态扩展。
2.2 四大核心组成结构(必考)
-
抽象产品(Product):定义产品统一行为规范,所有具体产品的顶层父类/接口
-
具体产品(ConcreteProduct):抽象产品的具体实现,对应不同业务产品
-
抽象工厂(Factory):定义工厂统一创建方法,规范产品产出接口
-
具体工厂(ConcreteFactory):实现抽象工厂方法,负责创建对应具体产品实例
2.3 解决的核心痛点
-
传统 new 硬编码耦合严重,修改产品类需要改动大量业务代码
-
多种同类产品创建逻辑混杂,代码臃肿、维护困难
-
新增产品需要修改原有工厂代码,违背开闭原则
-
产品创建逻辑分散,无法统一管控实例创建规则
2.4 完整实战可运行代码(面试手撕标准版)
业务场景:模拟支付渠道创建,支持微信支付、支付宝支付动态扩展,新增支付渠道无需修改原有代码。
① 抽象产品(支付接口)
java
// 抽象产品:统一支付产品规范
public interface Pay {
// 统一支付方法
void pay(double money);
}
② 具体产品(具体支付实现)
java
// 具体产品:支付宝支付
public class AliPay implements Pay {
@Override
public void pay(double money) {
System.out.println("支付宝支付成功,支付金额:" + money + " 元");
}
}
// 具体产品:微信支付
public class WechatPay implements Pay {
@Override
public void pay(double money) {
System.out.println("微信支付成功,支付金额:" + money + " 元");
}
}
③ 抽象工厂(工厂规范)
java
// 抽象工厂:定义产品创建规范
public interface PayFactory {
// 工厂方法:生产支付产品
Pay createPay();
}
④ 具体工厂(对应产品工厂)
java
// 支付宝工厂:只生产支付宝产品(一厂一品)
public class AliPayFactory implements PayFactory {
@Override
public Pay createPay() {
return new AliPay();
}
}
// 微信工厂:只生产微信产品(一厂一品)
public class WechatPayFactory implements PayFactory {
@Override
public Pay createPay() {
return new WechatPay();
}
}
⑤ 客户端测试调用
java
public class FactoryClient {
public static void main(String[] args) {
// 使用支付宝工厂创建产品
PayFactory aliFactory = new AliPayFactory();
Pay aliPay = aliFactory.createPay();
aliPay.pay(99.9);
// 使用微信工厂创建产品
PayFactory wechatFactory = new WechatPayFactory();
Pay wechatPay = wechatFactory.createPay();
wechatPay.pay(199.9);
}
}
扩展优势 :后续新增银行卡支付、云闪付,只需新增对应 Pay 实现类 + 对应工厂类,无需改动任何原有代码,完全符合开闭原则。
2.5 核心优缺点深度解析
✅ 优点
-
完全满足开闭原则:新增产品仅新增工厂和产品类,不修改成熟代码
-
创建与使用解耦:客户端只依赖抽象工厂、抽象产品,不依赖具体实现类
-
单一职责极致落地:一个工厂只负责一种产品的创建,职责单一清晰
-
便于单元测试与迭代维护:产品逻辑独立,互不影响
❌ 缺点
-
类爆炸问题严重:每新增一个产品,必须新增「产品类+工厂类」一对类,系统类数量激增
-
仅支持单一产品维度扩展:无法创建成套、多维度关联产品,能力受限
-
增加系统抽象复杂度:小型简单场景使用属于过度设计
2.6 工程落地真实场景
-
JDK 原生:
java.util.Calendar日历工厂、NumberFormat数字格式化工厂 -
Spring 源码:
BeanFactory顶层Bean工厂接口,子类实现不同Bean创建逻辑 -
业务场景:多文件解析器(Excel/Word/PDF解析工厂)、多日志实现(Logback/Log4j)、多支付渠道、多消息推送方式
-
中间件:不同数据源连接创建、不同缓存策略实例创建
2.7 面试高频核心问题(必背)
问题1:简单工厂和工厂方法模式的区别?
-
简单工厂 :一个统一工厂类,通过 if/switch 判断创建不同产品,新增产品需改工厂代码,违背开闭原则,属于编程习惯而非标准设计模式
-
工厂方法 :工厂抽象化,一厂一品,新增产品新增工厂,完全符合开闭原则,是标准创建型模式
问题2:工厂方法模式遵循了哪些设计原则?
核心遵循开闭原则、单一职责、依赖倒置、里氏替换。依赖抽象工厂而非具体工厂,子类工厂替换父类工厂无缝兼容,单一工厂单一职责,扩展不修改源码。
问题3:什么场景坚决不用工厂方法?
产品种类固定、几乎无迭代扩展、简单小型对象创建场景,使用工厂方法会徒增类复杂度,属于过度设计,直接 new 即可。
2.8 模式核心对比(工厂方法 VS 抽象工厂 前置铺垫)
工厂方法 :聚焦单一产品扩展,一厂一品,解决「同品类多实现」问题
抽象工厂 :聚焦产品族扩展,一厂多品,解决「成套组件配套兼容」问题
2.9 工程实战踩坑总结
-
坑1:产品极少迭代却强行使用,导致项目类冗余、架构臃肿
-
坑2:混淆简单工厂与工厂方法,面试表述错误,忽略开闭原则核心差异
-
坑3:客户端直接依赖具体工厂类,丧失解耦能力,违背依赖倒置原则
2.10 面试满分口述总结(万能话术)
工厂方法模式是标准的创建型设计模式,通过抽象工厂定义产品创建规范,将具体产品的实例化延迟到子类工厂实现,做到一厂一品、单一产品维度插拔式扩展。核心解决了传统new硬编码耦合、产品创建逻辑混乱、迭代修改源码的问题,严格遵循开闭原则与依赖倒置原则。缺点是会造成类数量膨胀,适用于同类产品多实现、需要频繁扩展的业务场景,也是Spring BeanFactory等框架底层的核心设计思想。
官方定义 :定义一个创建产品的抽象工厂接口,将产品实例化延迟到具体工厂子类,一厂一品、单一维度扩展。
核心结构:抽象工厂、具体工厂、抽象产品、具体产品
核心解决痛点:消灭业务代码硬编码 new、产品扩展需要大面积改代码、创建逻辑与业务逻辑耦合严重。
核心优缺点
-
✅ 优点:完全符合开闭原则、新增产品只新增工厂类、创建与使用解耦、便于维护
-
❌ 缺点:每新增一个产品就要新增一对工厂/产品类,导致类数量膨胀
工程落地场景:多文件解析器、多日志实现、基础支付渠道、JDK Calendar、BeanFactory
核心特征 :单一产品维度扩展,一个工厂只负责一种产品
面试标准答案:工厂方法模式通过抽象工厂定义创建规范,由具体工厂实现实例化,将产品创建延迟到子类,实现创建与业务解耦,支持单一产品维度插拔式扩展,满足开闭原则。
3. 抽象工厂模式(Abstract Factory)------ 产品族成套工厂
3.1 核心定义与底层思想
官方标准定义 :抽象工厂模式提供一个顶层抽象工厂接口,用于创建一系列相互关联、相互依赖的产品族,无需指定具体产品,同时保证同一产品族内的多组件配套兼容、成套替换。
通俗解读 :工厂方法是「一厂造一个产品」,抽象工厂是「一厂造一套产品」。专门解决多品类、多维度、成套组件适配的业务场景,保证成套产品统一匹配,杜绝组件混搭错乱。
核心底层思想 :区分产品等级 + 产品族 ,锁定整套组件体系,实现整套架构无缝替换,单一产品族内高度兼容。
3.2 两大核心概念(面试必懂|区分核心难点)
① 产品等级:同一功能、不同实现的单一产品(如:MySQL连接、Oracle连接属于同一个数据库产品等级)
② 产品族:同一整套体系下的多个不同等级产品(如:MySQL全套组件:MySQL连接、MySQL语句、MySQL事务,属于一个产品族)
核心定位:工厂方法只扩展「产品等级」;抽象工厂专门管控「产品族整套体系」。
3.3 四大核心组成结构
-
抽象产品(多组):定义多个不同等级的产品通用接口(多套产品规范)
-
具体产品:每个产品族下,对应各个等级的具体实现
-
抽象工厂:定义多个工厂方法,分别创建不同等级产品,规范整套产出
-
具体工厂:对应一个完整产品族,实现所有工厂方法,产出整套兼容产品
3.4 解决的核心痛点(工程核心价值)
-
多品类成套组件场景,单个工厂无法保证组件配套统一,容易出现混搭报错
-
多套业务体系切换繁琐,需要逐个替换单个产品,迭代成本极高
-
工厂方法只能扩展单一产品,无法支撑多维度、成套化架构设计
-
组件依赖混乱,不同体系产品混用导致系统兼容Bug、维护难度爆炸
3.5 完整实战可运行代码(面试手撕标准版)
业务场景 :模拟多数据库整套组件架构,存在两套产品族:MySQL数据库整套组件、Oracle数据库整套组件;包含两大产品等级:数据库连接、数据库事务。保证同一工厂产出的组件完全兼容,杜绝MySQL事务搭配Oracle连接的错乱问题。
① 多组抽象产品(两个产品等级:连接、事务)
java
// 抽象产品1:数据库连接(产品等级1)
public interface DBConnection {
// 统一连接方法
void connect();
}
// 抽象产品2:数据库事务(产品等级2)
public interface DBTransaction {
// 统一事务方法
void transaction();
}
② 具体产品(分产品族实现)
java
// ========== MySQL产品族具体产品 ==========
public class MysqlConnection implements DBConnection {
@Override
public void connect() {
System.out.println("创建 MySQL 数据库连接");
}
}
public class MysqlTransaction implements DBTransaction {
@Override
public void transaction() {
System.out.println("开启 MySQL 事务管理");
}
}
// ========== Oracle产品族具体产品 ==========
public class OracleConnection implements DBConnection {
@Override
public void connect() {
System.out.println("创建 Oracle 数据库连接");
}
}
public class OracleTransaction implements DBTransaction {
@Override
public void transaction() {
System.out.println("开启 Oracle 事务管理");
}
}
③ 抽象工厂(定义整套产品创建规范,多方法适配多等级)
java
// 抽象工厂:管控整套数据库组件
public interface DBFactory {
// 创建连接产品
DBConnection createConnection();
// 创建事务产品
DBTransaction createTransaction();
}
④ 具体工厂(一个工厂对应一整套产品族)
java
// MySQL工厂:只生产MySQL整套兼容组件
public class MysqlDBFactory implements DBFactory {
@Override
public DBConnection createConnection() {
return new MysqlConnection();
}
@Override
public DBTransaction createTransaction() {
return new MysqlTransaction();
}
}
// Oracle工厂:只生产Oracle整套兼容组件
public class OracleDBFactory implements DBFactory {
@Override
public DBConnection createConnection() {
return new OracleConnection();
}
@Override
public DBTransaction createTransaction() {
return new OracleTransaction();
}
}
⑤ 客户端测试调用(整套无缝切换)
java
public class AbstractFactoryClient {
public static void main(String[] args) {
// 切换MySQL整套体系,所有组件自动兼容
DBFactory mysqlFactory = new MysqlDBFactory();
mysqlFactory.createConnection().connect();
mysqlFactory.createTransaction().transaction();
System.out.println("==========整套切换数据库体系==========");
// 切换Oracle整套体系,无需逐个替换组件
DBFactory oracleFactory = new OracleDBFactory();
oracleFactory.createConnection().connect();
oracleFactory.createTransaction().transaction();
}
}
扩展说明 :新增PostgreSQL数据库体系,只需新增一套PostgreSQL产品类 + 对应工厂类,整套架构独立;但如果新增「数据库语句」新的产品等级,则需要修改抽象工厂接口,违背开闭原则。
3.6 核心优缺点深度解析(面试深挖)
✅ 优点
-
保证产品族组件兼容性:同一工厂产出的所有组件天然配套,杜绝混搭异常
-
整套体系无缝替换:切换业务架构只需切换工厂类,上层代码零改动
-
高度解耦:业务层依赖抽象工厂与抽象产品,完全不依赖具体实现
-
符合产品族维度开闭原则:新增整套产品族,无需修改原有代码
❌ 缺点(面试核心坑点)
-
产品等级扩展极其困难 :新增一个新的产品功能维度,需要修改抽象工厂接口,所有具体工厂全部重写,严重违背开闭原则
-
类爆炸问题更严重:多产品等级+多产品族,类数量成倍激增
-
简单场景过度设计:单一产品场景使用,徒增架构复杂度
3.7 工程落地真实场景
-
框架源码:Spring ApplicationContext 抽象工厂、MyBatis 多环境整套配置工厂
-
中间件场景:多数据源整套连接组件、多缓存体系(Redis/Memcached整套操作组件)
-
业务场景:多端UI成套组件(移动端/PC端整套控件)、多渠道消息推送(微信/短信/邮件整套推送组件)
-
系统适配:新旧版本系统整套接口适配、多租户整套业务组件隔离
3.8 高频面试核心问答(必背)
问题1:工厂方法和抽象工厂的核心区别?(必考)
-
工厂方法模式 :单一产品维度,一个工厂只创建一种产品,解决「同产品多实现」的扩展问题,支持产品等级扩展
-
抽象工厂模式 :多产品族维度,一个工厂创建一整套关联产品,解决「成套组件兼容、整套架构替换」问题,支持产品族扩展
问题2:抽象工厂最大的缺陷是什么?
最大缺陷是产品等级横向扩展困难。如果需要新增一个全新的产品功能维度,必须修改顶层抽象工厂接口,强制所有已有工厂实现新方法,破坏开闭原则,这也是工程中极少大面积使用抽象工厂的核心原因。
问题3:什么场景用抽象工厂?什么场景用工厂方法?
-
用工厂方法:产品单一、仅需扩展同种功能的不同实现
-
用抽象工厂:存在多组关联组件、需要保证组件配套兼容、需要整套架构切换
3.9 实战踩坑总结(工程避坑)
-
坑1:单一产品场景滥用抽象工厂,架构过度复杂,维护成本飙升
-
坑2:业务频繁新增产品维度,强行使用抽象工厂,导致频繁改顶层接口、全量工厂迭代
-
坑3:混淆产品族与产品等级,导致面试答题、代码设计逻辑错乱
-
坑4:忽略组件兼容核心价值,丧失抽象工厂最核心的设计意义
3.10 模式横向对比(创建型三工厂终极区分)
-
简单工厂:非标准模式,单类判断创建,改源码、不满足开闭
-
工厂方法:标准模式,一厂一品,扩展单个产品,满足开闭
-
抽象工厂:标准模式,一厂一套,扩展产品族,保证组件兼容,产品维度扩展受限
3.11 面试满分口述万能总结
抽象工厂模式是创建型模式的高阶工厂设计,核心是管控产品族、产出成套兼容组件。通过顶层抽象工厂定义多产品创建规范,具体工厂对应一整套业务体系,实现整套组件的统一创建与无缝替换,完美解决多组件混搭不兼容、体系切换繁琐的问题。其优点是保证产品族配套兼容、架构解耦、整套可扩展;核心缺陷是横向新增产品维度极其困难,违背开闭原则。适用于多成套组件、多体系适配、需要整体切换架构的业务与框架场景。
官方定义 :提供一个抽象工厂超级接口,让具体工厂可以创建一整套相互关联、相互依赖的产品族,保证成套组件兼容匹配。
核心解决痛点:多产品体系下,单个工厂无法保证组件配套统一、多产品组合混乱、适配不统一。
与工厂方法核心区别(面试必问)
-
工厂方法:单产品扩展,一厂一品
-
抽象工厂:产品族扩展,一厂多品、成套适配
核心优缺点
-
✅ 优点:保证成套产品统一适配、产品族整体替换、高层业务完全解耦
-
❌ 缺点:新增产品维度极其困难,违背开闭原则,扩展性差
工程落地场景:多数据库整套驱动组件、多端UI成套组件、Spring ApplicationContext、多渠道消息推送整套组件
面试标准答案:抽象工厂模式用于创建成套产品族,可整体替换整套组件,保证多产品体系配套兼容,适合多维度、成套组件的系统架构设计。
4. 建造者模式(Builder)------ 复杂对象分步组装
4.1 核心定义与底层思想
官方标准定义 :建造者模式属于创建型设计模式,将复杂对象的构建过程与对象的内部表示分离,通过分步、可选、链式的组装方式,使用相同的构建流程可以创建出不同配置、不同属性组合的复杂对象。
通俗解读 :针对参数多、参数可选、构造复杂、组合多样的对象,抛弃臃肿的重载构造方法、散乱的setter赋值,通过专门的建造类分步组装对象,支持灵活选配参数、链式调用,代码整洁且构建过程可控。
核心底层思维 :拆分构建步骤、统一构建骨架、差异化配置参数。固定对象构建流程,动态选配对象属性,实现复杂对象标准化、灵活化创建。
核心解决痛点:
-
构造方法臃肿爆炸:多参数对象需要编写大量重载构造方法,维护成本极高
-
setter赋值不规范:零散setter赋值导致对象创建状态不统一、参数遗漏、赋值顺序混乱
-
参数语义模糊:多参数构造方法传参极易传错顺序、传空参数,隐性Bug多
-
对象创建不可控:无法区分必填参数与选填参数,无法统一对象构建规范
4.2 四大核心组成组件(面试必考)
-
产品角色(Product):需要构建的复杂实体类,包含大量属性、必填+选填参数,是最终生成的目标对象
-
抽象建造者(Builder):定义统一的对象构建抽象方法,规范分步构建流程、提供产品获取抽象方法
-
具体建造者(ConcreteBuilder):实现抽象建造者的所有方法,完成具体参数赋值、分步组装,构建最终产品
-
指挥者(Director):【可选】统一调度构建步骤,固定对象构建流程,隔离客户端与具体构建细节,简单场景可省略,由客户端直接调用建造者
4.3 完整实战可运行代码(原生标准写法|面试手撕版)
业务场景:构建复杂电脑对象,包含必填参数(CPU、内存)、选填参数(硬盘、显卡、系统),模拟复杂多参数对象分步组装,支持灵活选配配置。
① 产品实体类(复杂目标对象)
java
/**
* 复杂产品:电脑实体类
* 包含必填参数 + 大量选填参数,适合建造者模式构建
*/
public class Computer {
// 必填参数
private String cpu;
private String memory;
// 选填参数
private String disk;
private String graphics;
private String system;
// 私有化构造方法,仅允许建造者构建
private Computer(ComputerBuilder builder) {
this.cpu = builder.cpu;
this.memory = builder.memory;
this.disk = builder.disk;
this.graphics = builder.graphics;
this.system = builder.system;
}
// Getter方法
public String getCpu() { return cpu; }
public String getMemory() { return memory; }
public String getDisk() { return disk; }
public String getGraphics() { return graphics; }
public String getSystem() { return system; }
@Override
public String toString() {
return "电脑配置{" +
"CPU='" + cpu + '\'' +
", 内存='" + memory + '\'' +
", 硬盘='" + disk + '\'' +
", 显卡='" + graphics + '\'' +
", 系统='" + system + '\'' +
'}';
}
}
② 抽象建造者(统一构建规范)
java
/**
* 抽象建造者:定义电脑构建统一步骤
*/
public abstract class ComputerBuilder {
// 抽象构建CPU
public abstract ComputerBuilder buildCpu(String cpu);
// 抽象构建内存
public abstract ComputerBuilder buildMemory(String memory);
// 抽象构建硬盘
public abstract ComputerBuilder buildDisk(String disk);
// 抽象构建显卡
public abstract ComputerBuilder buildGraphics(String graphics);
// 抽象构建系统
public abstract ComputerBuilder buildSystem(String system);
// 获取最终构建产品
public abstract Computer build();
}
③ 具体建造者(实现分步构建|链式调用)
java
/**
* 具体建造者:电脑具体构建实现
* 支持链式调用、参数灵活选配
*/
public class ConcreteComputerBuilder extends ComputerBuilder {
// 待构建的产品属性
private String cpu;
private String memory;
private String disk;
private String graphics;
private String system;
@Override
public ConcreteComputerBuilder buildCpu(String cpu) {
this.cpu = cpu;
return this;
}
@Override
public ConcreteComputerBuilder buildMemory(String memory) {
this.memory = memory;
return this;
}
@Override
public ConcreteComputerBuilder buildDisk(String disk) {
this.disk = disk;
return this;
}
@Override
public ConcreteComputerBuilder buildGraphics(String graphics) {
this.graphics = graphics;
return this;
}
@Override
public ConcreteComputerBuilder buildSystem(String system) {
this.system = system;
return this;
}
// 最终组装生成产品
@Override
public Computer build() {
// 可在校验必填参数,保证对象合法性
if (cpu == null || memory == null) {
throw new RuntimeException("CPU、内存为必填配置,不能为空");
}
return new Computer(this);
}
}
④ 客户端测试调用
java
public class BuilderClient {
public static void main(String[] args) {
// 灵活选配参数,链式构建高配电脑
Computer highConfigPc = new ConcreteComputerBuilder()
.buildCpu("Intel i9")
.buildMemory("32G")
.buildDisk("1T固态")
.buildGraphics("RTX 4090")
.buildSystem("Windows11")
.build();
// 仅必填参数,构建低配电脑,无需改动构建逻辑
Computer lowConfigPc = new ConcreteComputerBuilder()
.buildCpu("Intel i5")
.buildMemory("16G")
.build();
System.out.println("高配电脑:" + highConfigPc);
System.out.println("低配电脑:" + lowConfigPc);
}
}
4.4 极简常用写法(Lombok @Builder|工程实战首选)
实际开发中无需手写完整建造者模板,通过Lombok注解一键实现建造者模式,兼顾简洁性与实用性,是工程主流方案。
java
import lombok.Builder;
import lombok.ToString;
/**
* Lombok 极简建造者模式(工程常用)
* 自动生成建造者、链式调用、build方法
*/
@Builder
@ToString
public class User {
// 必填+选填参数
private Long id;
private String username;
private String password;
private String phone;
private String address;
// 自带建造者链式调用
public static void main(String[] args) {
User user = User.builder()
.id(1001L)
.username("test")
.password("123456")
.phone("13800138000")
.build();
System.out.println(user);
}
}
4.5 核心优缺点深度解析
✅ 核心优点
-
参数灵活可控:完美区分必填、选填参数,按需组装,无需重载大量构造方法
-
代码可读性极强:链式调用语义清晰,直观看到每个参数赋值,杜绝传参错乱问题
-
构建与解耦分离:对象构建流程与业务表示分离,相同构建流程可生成不同对象
-
对象创建安全:可在build方法做参数校验,保证最终生成对象的合法性、完整性
-
单一职责落地:构建逻辑统一交由建造者处理,产品类专注自身属性与业务逻辑
❌ 核心缺点
-
类结构冗余:原生手写需要新增抽象建造者、具体建造者,类数量增多,代码量大
-
简单对象过度设计:少量参数的简单对象使用建造者,徒增架构复杂度
-
属性同步繁琐:产品类新增属性,需要同步修改建造者所有构建方法
4.6 工程落地真实场景(高频)
-
JDK原生场景 :
StringBuilder、StringBuffer字符串分步拼接构建 -
框架源码场景 :MyBatis
SqlSessionFactoryBuilder、Spring 配置类构建、Redis客户端参数配置构建 -
业务开发场景:多参数复杂DTO/VO、订单复杂对象、用户信息、配置参数类构建
-
中间件场景:SQL语句拼接、请求报文组装、多参数配置类初始化
4.7 高频面试核心问答(必背)
问题1:建造者模式和工厂模式的核心区别?(必考)
-
核心侧重点不同 :工厂模式专注快速产出完整实例 ,关注「对象有没有」;建造者模式专注分步精细组装,关注「对象好不好、参数全不全」
-
适用场景不同 :工厂模式适配普通对象、单一产出对象;建造者适配多参数、可选参数、复杂组装对象
-
扩展能力不同 :工厂模式侧重产品维度扩展;建造者侧重对象配置组合扩展,相同流程不同配置
问题2:建造者模式为什么能解决多参数构造混乱问题?
传统构造方法依靠参数顺序传值,多参数场景语义模糊、极易传错;零散setter无法保证必填参数完整性。建造者通过链式命名赋值、分步构建、最终统一校验,既保证参数语义清晰,又能管控参数合法性,彻底解决多参数构建乱象。
问题3:指挥者(Director)是否必须存在?
非必须。指挥者用于固定构建流程、统一调度步骤,适合构建流程固定、步骤严苛的场景;日常开发中为简化代码,可直接由客户端调用具体建造者,省略指挥者,不影响核心功能。
4.8 实战踩坑总结(工程避坑)
-
坑1:简单对象滥用建造者:2-3个参数的简单对象强行使用,造成代码冗余、过度设计
-
坑2:省略参数校验:build方法不做必填参数校验,依然会生成不完整的无效对象
-
坑3:属性不同步:产品类新增/删除属性,未同步更新建造者构建方法,导致赋值缺失
-
坑4:混淆工厂与建造者:复杂单一实例创建用建造者,多品类产品扩展用工厂,切勿混用
4.9 面试满分终极总结(万能口述话术)
建造者模式是创建型模式中专门解决复杂多参数对象构建混乱的核心模式,核心思想是分离对象的构建过程与内部表示,通过分步组装、链式调用、参数选配的方式,灵活创建不同配置的复杂对象。相比传统构造方法和setter赋值,它语义清晰、参数可控、可校验对象合法性,完美解决构造方法臃肿、传参错误、对象状态不可控的问题。工程中普遍使用Lombok @Builder简化开发,适用于多参数、可选参数、配置多样的复杂对象构建场景,与工厂模式侧重实例产出不同,建造者更专注对象的精细化分步组装。
官方极简定义:分离复杂对象的构建与表示,通过分步组装,实现不同配置复杂对象的灵活创建。
核心特征:分步构建、链式调用、参数可选、配置灵活
面试标准答案:建造者模式将复杂对象的构建过程与内部表示解耦,通过分步组装和链式配置灵活构建多参数对象,解决了构造方法臃肿、传参混乱、对象创建不规范的问题,适合复杂配置类对象的创建场景。
官方定义 :将复杂对象的构建过程与对象的内部表示分离,通过分步、灵活、可选参数组装,可创建不同配置、不同表现的复杂对象。
核心结构:产品Product、抽象建造者、具体建造者、指挥者Director(可选)
核心解决痛点:多参数复杂对象构造混乱、重载构造方法爆炸、参数传参易错、对象构建不可控。
核心优缺点
-
✅ 优点:分步构建、参数灵活选配、构建逻辑清晰、代码可读性极高、支持链式调用
-
❌ 缺点:结构复杂、类数量多,简单对象使用过度设计
工程落地场景:Lombok @Builder、StringBuilder、SQL拼接、复杂DTO/VO构建、MyBatis SqlSessionFactoryBuilder、Redis参数配置构建
与工厂模式区别 :工厂关注「产出实例」,建造者关注「分步组装、精细配置」
面试标准答案:建造者模式分离复杂对象的构建与表示,通过分步组装、链式配置灵活构建多参数复杂对象,解决构造方法臃肿、参数混乱问题,大幅提升复杂对象构建代码可读性。
5. 原型模式(Prototype)------ 克隆快速创建对象
5.1 核心定义与底层思想
官方标准定义 :原型模式属于创建型设计模式,指通过复制、克隆已有原型对象的方式创建全新对象,替代传统new关键字实例化的创建方式,基于原有模板对象快速生成复用实例。
通俗解读 :提前创建好一个模板对象,后续需要新对象时,直接拷贝模板生成,无需重复执行复杂的初始化、参数赋值、资源加载逻辑,实现一次创建、多次复用、快速生成。
核心底层思想 :对象复用、以克隆代新建。剥离重复的对象初始化逻辑,基于已有对象快照复制新实例,大幅降低复杂对象创建开销。
核心解决痛点:
-
复杂大对象通过new创建时,初始化逻辑繁琐、参数赋值冗余、性能开销大
-
大量相似对象重复创建,造成代码冗余、资源浪费
-
对象创建流程固定、模板统一,无需每次从头构建
-
频繁创建销毁对象,导致JVM GC频繁、系统性能下降
5.2 两大核心拷贝方式(面试核心重难点)
原型模式的核心是对象克隆,分为浅拷贝、深拷贝两种,核心差异集中在引用类型属性的复制逻辑,是面试必考知识点。
① 浅拷贝(Shallow Copy)
核心规则 :仅复制对象基本数据类型 的数值,对于引用类型属性,只复制内存地址,新旧对象共享同一个引用对象。
底层原理:JDK默认clone()方法原生实现浅拷贝,直接拷贝对象内存二进制数据,不递归处理引用属性。
致命问题:修改新对象的引用类型属性,会同步影响原原型对象,引发数据错乱、线程数据污染问题。
② 深拷贝(Deep Copy)
核心规则 :不仅复制基本数据类型,所有引用类型属性递归完整复制,新旧对象的所有属性完全独立,内存地址互不干扰。
底层原理:手动重写clone方法递归克隆引用对象,或通过序列化、JSON序列化实现全量对象复制,彻底隔离新旧对象。
核心优势:对象完全独立,修改任意对象属性,不会影响对方,无数据污染风险,工程开发首选。
5.3 完整实战可运行代码(面试手撕标准版)
业务场景:创建复杂用户对象,包含基本类型属性(姓名、年龄)和引用类型属性(地址),分别实现浅拷贝、深拷贝,直观对比差异。
① 基础实体类(实现Cloneable接口)
java
/**
* 地址实体(引用类型属性)
*/
public class Address implements Cloneable {
private String province;
private String city;
public Address(String province, String city) {
this.province = province;
this.city = city;
}
// 重写clone方法,支持深拷贝递归复制
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// getter/setter
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
/**
* 用户原型对象
* 实现Cloneable接口:标识可克隆,是clone()方法生效的前提
*/
public class User implements Cloneable {
// 基本类型属性
private String username;
private Integer age;
// 引用类型属性
private Address address;
public User(String username, Integer age, Address address) {
this.username = username;
this.age = age;
this.address = address;
}
// 【浅拷贝】JDK默认原生实现
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 【深拷贝】手动递归实现
public User deepClone() throws CloneNotSupportedException {
// 1. 先拷贝基本类型+引用地址
User user = (User) super.clone();
// 2. 递归克隆引用对象,生成全新地址实例
user.address = (Address) address.clone();
return user;
}
// getter/setter
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
② 客户端测试(浅拷贝数据污染演示)
java
public class PrototypeClient {
public static void main(String[] args) throws CloneNotSupportedException {
// 1. 创建原始原型对象
Address address = new Address("广东省", "深圳市");
User oldUser = new User("张三", 25, address);
System.out.println("原始对象:" + oldUser);
// 2. 执行浅拷贝
User shallowUser = (User) oldUser.clone();
// 修改拷贝对象的引用类型属性
shallowUser.getAddress().setCity("广州市");
shallowUser.setUsername("浅拷贝用户");
System.out.println("浅拷贝后原始对象:" + oldUser);
System.out.println("浅拷贝生成对象:" + shallowUser);
// 结论:引用类型数据被同步修改,发生数据污染
// 3. 重置数据,执行深拷贝
oldUser.getAddress().setCity("深圳市");
User deepUser = oldUser.deepClone();
// 修改深拷贝对象属性
deepUser.getAddress().setCity("珠海市");
deepUser.setUsername("深拷贝用户");
System.out.println("==========深拷贝隔离效果==========");
System.out.println("深拷贝后原始对象:" + oldUser);
System.out.println("深拷贝生成对象:" + deepUser);
// 结论:新旧对象完全独立,无数据污染
}
}
③ 序列化实现终极深拷贝(工程通用方案)
对于多层嵌套、复杂对象,手动递归克隆繁琐易错,通过序列化+反序列化实现全自动深拷贝,适配所有复杂对象场景。
java
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 序列化实现深拷贝工具类(工程通用)
* 要求:目标对象及所有引用属性实现Serializable序列化接口
*/
public class PrototypeDeepCopyUtil {
// 序列化深拷贝通用方法
public static <T extends Serializable> T deepCopy(T obj) throws Exception {
// 序列化:对象转字节数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
// 反序列化:字节数组转全新对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
}
}
5.4 核心优缺点深度解析
✅ 核心优点
-
创建性能极致高效:直接内存拷贝对象,无需执行繁琐的初始化、赋值逻辑,比new创建性能高出数倍
-
代码极简复用:一次定义原型模板,无限次克隆复用,杜绝重复初始化代码
-
对象创建解耦:客户端无需关注对象创建细节、参数配置,直接克隆模板即可获取完整实例
-
动态批量创建:可根据业务需求,批量生成相似对象,适配模板化业务场景
❌ 核心缺点
-
深浅拷贝隐患极大:浅拷贝引用类型共享内存,易引发数据污染,极易产生线上隐性Bug
-
复杂对象克隆繁琐:多层嵌套对象需要手动递归或序列化实现深拷贝,开发成本较高
-
需维护原型模板:需要提前初始化、维护全局原型对象,占用常驻内存
-
不适合动态差异大对象:对象每次创建参数差异极大、无模板共性,不适合使用原型模式
5.5 工程落地真实高频场景
-
Spring框架核心应用 :Bean作用域 prototype原型Bean,每次请求均克隆生成全新Bean实例
-
模板化业务场景:报表模板、表单模板、订单模板、审批流程模板批量复制生成
-
高性能对象创建:大数据量DTO、复杂配置类、缓存副本对象快速创建
-
资源复用场景:线程任务模板、消息推送模板、日志模板克隆复用
-
JDK原生场景:ArrayList、HashMap等集合容器clone方法、对象数组批量复制
5.6 面试高频核心难题(必背)
问题1:浅拷贝和深拷贝的核心区别?(必考)
-
浅拷贝:复制基本类型值,引用类型仅复制内存地址,新旧对象共享引用资源,修改互相影响,JDK clone()默认实现
-
深拷贝:基本类型、引用类型全量完整复制,递归克隆嵌套对象,新旧对象完全独立,互不干扰,需手动实现或序列化实现
问题2:原型模式和new创建对象的区别?
-
new创建:先分配内存、执行构造方法初始化、赋值,流程繁琐,性能低,适合全新差异化对象
-
原型克隆:直接内存拷贝已有完整对象,跳过初始化流程,性能极高,适合模板化、相似度高的对象
问题3:Cloneable接口的作用?为什么是空接口?
Cloneable是标识空接口 ,无任何抽象方法,核心作用是告知JVM当前类允许克隆 。若类未实现该接口,调用clone()方法会直接抛出 CloneNotSupportedException 异常,是JVM层面的克隆权限校验标识。
问题4:Spring原型Bean的底层原理?
Spring prototype作用域Bean,容器不会缓存Bean实例,每次从容器获取Bean时,都会基于Bean定义模板,通过反射/克隆方式生成全新实例,对应原型模式核心思想,保证每次获取都是新对象,无共享变量并发问题。
5.7 实战踩坑总结(工程落地避坑)
-
坑1:默认使用浅拷贝:忽略引用类型共享问题,修改拷贝对象导致原模板数据异常,引发线上隐性Bug
-
坑2:未实现Cloneable接口:直接调用clone()方法,抛出克隆不支持异常
-
坑3:复杂对象不做深拷贝:多层嵌套对象仅做浅拷贝,多层引用对象全部共享,数据污染范围扩大
-
坑4:滥用原型模式:无模板共性、每次创建差异极大的对象,强行使用克隆,徒增代码复杂度
5.8 面试满分终极口述总结(万能话术)
原型模式是高性能的创建型模式,核心通过克隆已有原型对象替代new新建,跳过繁琐的对象初始化流程,实现模板化对象快速创建。分为浅拷贝和深拷贝,浅拷贝仅复制基本类型、共享引用类型,存在数据污染风险;深拷贝实现全量对象独立复制,是工程开发首选。该模式核心优势是创建性能高、代码复用性强,适合模板化、大批量、相似度高的复杂对象创建场景,典型应用为Spring原型Bean、各类业务模板批量生成,开发中需重点规避浅拷贝的引用类型数据污染问题。
官方极简定义:通过克隆已有原型对象快速创建新实例,实现模板化对象高效复用。
核心特征:内存拷贝、模板复用、高性能创建、分深浅拷贝
核心解决痛点:复杂对象new初始化性能低、代码冗余、重复创建资源浪费
核心优缺点
-
✅ 优点:创建性能极高、模板复用、解耦创建细节、支持批量生成对象
-
❌ 缺点:浅拷贝存在数据污染、复杂对象深拷贝开发繁琐、需维护原型模板
工程落地场景:Spring prototype原型Bean、业务模板批量复制、复杂配置/报表对象创建、JDK集合克隆
面试标准答案:原型模式基于对象克隆机制,以已有原型模板快速生成新对象,规避了传统new初始化的性能开销,大幅提升模板化对象的创建效率,通过深浅拷贝适配不同业务场景,广泛应用于框架源码和批量对象创建场景。
官方定义 :通过拷贝已有原型对象创建新对象,代替传统new构造实例,实现对象快速复用创建。
两种拷贝方式
-
浅拷贝:基本类型复制,引用类型共用同一对象
-
深拷贝:基本+引用类型全部全新复制,完全独立对象
核心解决痛点:大对象、复杂对象new创建开销大、重复初始化耗性能、模板对象重复构建冗余。
核心优缺点
-
✅ 优点:创建性能极高、规避重复初始化、快速批量生成对象、模板复用性强
-
❌ 缺点:深浅拷贝容易出错、引用类型处理复杂、需要重写克隆方法
工程落地场景:Spring scope="prototype"原型Bean、大数据模板对象、报表模板复制、缓存副本、配置模板克隆
面试易错点 :必须区分深浅拷贝差异、引用类型拷贝风险
面试标准答案:原型模式基于对象克隆实现快速实例创建,规避大对象new初始化开销,通过原型模板批量生成对象,大幅提升对象创建性能,适合模板化、大批量对象创建场景。
6. 创建型模式5种终极对比总结(面试秒杀|全维度完整版)
|-----------|-------------------------|--------------------------------------|---------------------------------------------|----------------|---------------|
| 模式 | 核心能力 | 核心解决痛点 | 适用场景 | 核心设计原则 | 面试秒杀一句话区分 |
| 单例模式 | 管控实例数量,全局唯一实例,统一资源入口 | 全局资源重复创建、实例不统一、内存资源浪费、状态混乱 | 全局配置类、工具类、连接池、Redis客户端、Spring单例Bean等无状态全局资源 | 单一职责、私有化封装 | 只造一个,全局复用 |
| 工厂方法 | 单产品维度扩展,子类工厂生产单一产品 | 简单工厂违背开闭原则,新增产品需修改源码,产品扩展不灵活 | 单一品类、多算法/多实现扩展场景,如多支付方式、多日志类型 | 开闭原则、依赖倒置 | 一厂一品,单品无限扩展 |
| 抽象工厂 | 管控产品族,批量创建成套关联组件,保证组件兼容 | 多组件体系混搭不兼容、整套架构切换繁琐、多产品耦合混乱 | 多成套组件场景,如多数据库驱动、多端UI组件、多渠道消息体系 | 开闭原则(产品族)、里氏替换 | 一厂一套,成套组件适配 |
| 建造者模式 | 复杂对象分步精细化组装,灵活选配参数,链式构建 | 多参数对象构造方法臃肿、传参错乱、setter赋值不规范、对象状态不可控 | 多参数、可选参数多、配置多样的复杂对象,如订单、DTO、系统配置、电脑配置 | 单一职责、封装隔离 | 分步拼装,精细定制复杂对象 |
| 原型模式 | 基于已有对象克隆,快速生成新实例,模板化复用 | 复杂大对象new初始化性能低、重复初始化逻辑冗余、批量创建资源消耗大 | 模板化对象、大批量相似对象、复杂配置对象,如Spring原型Bean、业务模板批量复制 | 复用优先、解耦创建 | 克隆复制,高速批量造对象 |
6.1 五大模式核心横向辨析(面试高频易错点)
-
单例 vs 其他创建型:唯一管控实例数量,不关注扩展与组装,核心是「资源独占、全局唯一」,无多实例扩展能力。
-
工厂方法 vs 抽象工厂 :工厂方法是单品纵向扩展 ,新增产品即新增工厂;抽象工厂是产品族横向成套扩展,侧重组件配套兼容,致命缺陷是新增产品维度困难。
-
工厂系列 vs 建造者:工厂关注「快速产出完整可用实例」,不关注对象内部参数细节;建造者关注「对象精细化组装、参数选配、构建流程可控」,只适配复杂多参数对象。
-
原型模式 vs new创建 :原型跳过构造方法初始化,内存直接拷贝,主打高性能、模板复用;new从头初始化,适配全新差异化对象创建。
6.2 创建型模式适配优先级口诀(面试秒选场景)
唯一资源用单例,单品扩展工厂依;成套组件抽象厂,复杂建造精细齐;模板批量克隆提
6.3 创建型模式终极满分总结(面试口述万能话术)
创建型包含单例、工厂方法、抽象工厂、建造者、原型五种模式,核心目的是彻底解耦对象创建与业务使用,杜绝new硬编码耦合。五种模式分工明确、层层互补:单例管控全局唯一实例,解决资源复用与统一管控问题;工厂方法与抽象工厂解决不同维度的产品扩展与组件兼容问题,完美适配开闭原则;建造者专注复杂多参数对象的标准化、精细化构建,解决构造混乱问题;原型模式依托克隆机制,实现模板化对象的高性能批量创建。所有创建型模式均基于面向对象四大特性、遵循六大设计原则,从实例数量、产品维度、构建流程、创建性能四大维度,全方位优化对象创建逻辑,是业务开发、框架源码底层最基础、最核心的设计体系。
创建型模式终极面试总结:五种创建型模式从「实例数量、产品维度、构建流程、创建性能」四个维度,全方位解决对象创建耦合问题,彻底剥离硬编码new,让对象创建统一、可控、可扩展、高性能。
三、第二大类:结构型模式(7种完整版|面试满分体系|代码+场景+真题)
大类总纲回顾 :结构型模式核心是通过组合、包装、适配、分层、复用的方式,重组已有类与对象结构 ,无需修改原有业务源码,即可实现系统兼容、功能增强、架构简化、内存优化。核心设计思想:多用组合、少用继承,彻底解决继承带来的类爆炸、架构僵硬、耦合过高问题。
核心解决痛点:接口不兼容、子系统调用复杂、继承层级臃肿、对象重复创建浪费内存、树形结构无法统一处理、功能扩展僵硬。
设计原则落地 :高度贴合迪米特法则、单一职责、依赖倒置、开闭原则。
7种模式精准分组回顾:
-
接口适配组:适配器(事后兼容)、桥接(事前解耦防类爆炸)
-
功能包装组:装饰器(动态叠加功能)、代理(管控对象访问)
-
架构简化组:外观门面(统一入口、简化调用)
-
性能结构组:享元(内存对象复用)、组合(树形结构统一处理)
1. 适配器模式(Adapter)------ 接口兼容转换器
1.1 核心定义与底层思想
官方标准定义 :将一个类的接口转换成客户端期望的另一个接口,使原本因接口不兼容、无法协同工作的类可以正常联动,属于事后兼容修复方案。
通俗解读:做一个"转换接头",适配新旧接口、第三方接口,抹平接口差异,让不匹配的接口正常对接,无需修改原有两方源码。
核心底层思想 :接口转换、兼容适配、解耦改造,基于组合/继承实现接口适配,最小改动实现系统兼容。
核心解决痛点:
-
新旧系统接口字段、方法定义不统一,无法直接对接
-
第三方SDK接口规范与本地业务接口不匹配
-
老代码接口过时,修改源码风险高、成本大
-
多渠道、多版本接口差异化,需要统一适配入口
1.2 两大实现方式(面试必考优劣对比)
① 类适配器(继承实现|不推荐)
通过继承被适配类、实现目标接口完成适配,静态绑定,适配逻辑固定。缺点是单继承限制大、无法适配多个类、扩展性极差,工程基本废弃,仅作面试知识点了解。
② 对象适配器(组合实现|工程首选)
通过组合持有被适配对象、实现目标接口完成适配,基于组合替代继承,灵活度极高,支持多对象适配、动态扩展,完全贴合"多用组合少用继承"核心准则,是实际开发唯一常用方案。
1.3 完整实战可运行代码(对象适配器|面试手撕版)
业务场景:本地系统统一充电接口(目标接口),适配老旧快充设备(被适配类),抹平新旧设备接口差异。
① 目标接口(客户端期望的规范)
java
/**
* 目标接口:本地统一快充接口
*/
public interface QuickCharge {
// 统一快充方法
void quickCharge();
}
② 被适配者(老旧不兼容设备)
java
/**
* 被适配者:老旧普通充电设备(接口不兼容)
*/
public class OldNormalCharge {
// 老旧设备只有普通充电方法,没有快充方法
public void normalCharge(){
System.out.println("老旧设备:普通慢速充电");
}
}
③ 适配器类(核心适配转换)
java
/**
* 适配器:充电适配转换器
* 组合持有被适配对象,实现目标接口,完成接口转换
*/
public class ChargeAdapter implements QuickCharge {
// 组合被适配的老旧设备对象
private OldNormalCharge oldCharge;
public ChargeAdapter(OldNormalCharge oldCharge) {
this.oldCharge = oldCharge;
}
// 适配转换:将普通充电适配为快充接口规范
@Override
public void quickCharge() {
System.out.println("适配器介入:接口适配转换,兼容老旧设备");
oldCharge.normalCharge();
}
}
④ 客户端测试调用
java
public class AdapterClient {
public static void main(String[] args) {
// 客户端只依赖统一目标接口,无需关心底层设备差异
QuickCharge charge = new ChargeAdapter(new OldNormalCharge());
charge.quickCharge();
}
}
1.4 核心优缺点深度解析
✅ 核心优点
-
兼容性极强:无需修改原有两方源码,通过适配器抹平接口差异,低风险实现系统兼容
-
职责单一:适配逻辑统一收敛,业务代码与兼容代码解耦
-
灵活扩展:对象适配器基于组合,可适配多个不同被适配类,扩展性远超继承
-
符合开闭原则:新增适配场景只需新增适配器,不改动原有代码
❌ 核心缺点
-
增加代码层级:额外新增适配器类,小幅增加系统复杂度
-
过度适配冗余:接口差异极小的简单场景使用,属于过度设计
1.5 工程落地高频场景
-
框架源码场景:SpringMVC HandlerAdapter(处理器适配器),适配不同类型Controller处理器
-
业务兼容场景:新旧版本接口适配、第三方支付/登录SDK接口适配、多渠道数据格式统一转换
-
技术迁移场景:老项目技术栈迁移、数据库驱动版本适配、中间件接口兼容
1.6 面试高频问答(必背)
问题1:类适配器和对象适配器的核心区别?
类适配器:基于继承实现,静态绑定,单继承限制、无法多适配、扩展性差
对象适配器:基于组合实现,动态绑定、无继承限制、支持多对象适配、灵活度高,工程首选
问题2:适配器和桥接模式的核心区别?
适配器是事后修复,解决现有不兼容接口的适配问题,属于被动兼容;
桥接模式是事前解耦,提前分离抽象与实现,避免后续接口耦合,属于主动架构优化。
1.7 面试满分终极总结(万能话术)
适配器模式是接口兼容的核心结构型模式,分为类适配器与对象适配器,工程以组合实现的对象适配器为主。核心通过中间适配层转换接口规范,在不修改原有源码的前提下,解决接口不兼容、系统无法对接的问题,职责单一、扩展灵活,广泛应用于框架适配、新旧系统兼容、第三方SDK对接场景,完美遵循开闭原则与组合复用思想。
2. 桥接模式(Bridge)------ 多维解耦防类爆炸
2.1 核心定义与底层思想
官方标准定义 :将抽象部分 与实现部分分离,使两者可以独立扩展、互不影响,通过组合桥接替代多层继承,彻底解决多维度继承导致的类爆炸问题。
通俗解读 :当系统存在两个及以上变化维度时,拆分维度、通过组合桥接关联,每个维度独立扩展,不用层层继承嵌套。
核心底层思想 :维度拆分、组合桥接、双向独立扩展,用组合替代臃肿的多层继承架构。
核心解决痛点:
-
多维度业务场景下,多层继承导致子类数量爆炸,代码臃肿难维护
-
抽象与实现强耦合,一方变更联动另一方,扩展性极差
-
单一继承体系无法支撑多维度灵活组合,架构僵硬
2.2 核心组成组件
-
抽象角色(Abstraction):顶层抽象维度,持有实现维度的引用,定义高层业务方法
-
扩充抽象角色:继承顶层抽象,扩展抽象维度的差异化能力
-
实现接口角色:底层实现维度统一规范,独立于抽象维度
-
具体实现角色:实现底层接口,提供差异化实现能力
2.3 完整实战可运行代码(面试手撕版)
业务场景 :消息推送系统,存在两个变化维度:消息类型(普通/紧急) 、推送渠道(短信/微信),通过桥接模式拆分维度,独立扩展、自由组合。
① 实现维度接口(推送渠道)
java
/**
* 实现维度:推送渠道接口(独立扩展)
*/
public interface PushChannel {
// 渠道推送方法
void push(String content);
}
② 具体实现类(各渠道实现)
java
// 短信推送渠道
public class SmsPush implements PushChannel{
@Override
public void push(String content) {
System.out.println("短信渠道推送:" + content);
}
}
// 微信推送渠道
public class WechatPush implements PushChannel{
@Override
public void push(String content) {
System.out.println("微信渠道推送:" + content);
}
}
③ 抽象维度顶层类(消息类型)
java
/**
* 抽象维度:消息顶层抽象,桥接推送渠道
*/
public abstract class Message {
// 组合实现维度,完成桥接
protected PushChannel pushChannel;
public Message(PushChannel pushChannel) {
this.pushChannel = pushChannel;
}
// 抽象消息发送方法
public abstract void send(String content);
}
④ 扩充抽象类(差异化消息类型)
java
// 普通消息
public class NormalMessage extends Message{
public NormalMessage(PushChannel pushChannel) {
super(pushChannel);
}
@Override
public void send(String content) {
pushChannel.push("【普通消息】" + content);
}
}
// 紧急消息
public class UrgentMessage extends Message{
public UrgentMessage(PushChannel pushChannel) {
super(pushChannel);
}
@Override
public void send(String content) {
pushChannel.push("【紧急消息】加急推送:" + content);
}
}
⑤ 客户端测试调用
java
public class BridgeClient {
public static void main(String[] args) {
// 自由组合维度:紧急消息+短信推送
Message urgentSms = new UrgentMessage(new SmsPush());
urgentSms.send("订单支付超时,请及时处理");
// 自由组合维度:普通消息+微信推送
Message normalWechat = new NormalMessage(new WechatPush());
normalWechat.send("系统每日通知:服务运行正常");
}
}
2.4 核心优缺点深度解析
✅ 核心优点
-
彻底杜绝类爆炸:拆分多变化维度,替代多层继承,子类数量大幅减少
-
双向独立扩展:抽象维度、实现维度可单独新增、单独修改,互不影响,完美适配开闭原则
-
灵活组合:不同维度实现可自由搭配组合,适配多样化业务场景
-
解耦彻底:抽象与实现完全分离,高层架构不依赖底层细节
❌ 核心缺点
-
架构复杂度提升:需要拆分维度、定义多层抽象,对设计能力要求高
-
不适合单维度场景:仅单一变化维度时使用,属于过度设计
2.5 工程落地高频场景
-
多维度业务场景:消息类型+推送渠道、支付类型+支付渠道、文件类型+解析方式
-
框架架构场景:JDBC驱动适配、日志框架多实现、多数据源兼容架构
-
UI设计场景:图形形状+颜色、按钮样式+尺寸多维度组合
2.6 面试高频问答(必背)
问题1:桥接模式核心解决什么问题?
核心解决多维度变化场景下的多层继承类爆炸问题,通过维度拆分+组合桥接,让抽象与实现独立扩展,实现灵活组合、低耦合架构。
问题2:桥接和适配器的本质区别?
桥接是事前架构设计,主动拆分维度、解耦扩展,用于新项目架构搭建;
适配器是事后兼容修复,解决已有接口不兼容问题,用于存量系统改造。
2.7 面试满分终极总结(万能话术)
桥接模式是解决多维度业务扩展的核心结构型模式,核心思想是拆分抽象与实现两大维度,通过组合桥接替代多层继承,让两个维度独立扩展、自由组合。彻底解决了多维度继承导致的类爆炸、架构僵硬、耦合过高问题,高度遵循开闭原则与组合复用思想,适用于存在两个及以上可变维度的复杂业务架构设计。
3. 装饰器模式(Decorator)------ 动态功能增强
3.1 核心定义与底层思想
官方标准定义:动态给原有对象叠加额外功能,无需修改原有对象源码、无需新增大量子类,实现功能的灵活扩展与多层增强,是继承扩展的最优替代方案。
通俗解读:给原有对象"穿外套",层层包装、层层增强,原有核心功能不变,按需叠加新能力,支持动态组合、多层叠加。
核心底层思想 :同类型包装、动态增强、功能叠加、无侵入扩展
核心解决痛点:
-
继承扩展功能导致子类泛滥,类爆炸严重
-
静态继承无法动态增减功能,扩展僵硬
-
原有核心功能与扩展功能耦合,修改影响核心逻辑
3.2 核心特征(面试秒杀识别点)
核心标识 :装饰类与被装饰类实现同一个顶层接口,装饰类内部组合持有被装饰对象,实现同类型嵌套包装、多层增强。
3.3 完整实战可运行代码(面试手撕版)
业务场景:咖啡饮品制作,基础咖啡为核心功能,可动态叠加加糖、加牛奶、加冰等附加功能,灵活组合不同饮品。
① 统一顶层接口(饮品规范)
java
/**
* 顶层统一接口:所有饮品+装饰器实现该接口
*/
public interface Drink {
// 获取价格
double getPrice();
// 获取饮品描述
String getDesc();
}
② 具体被装饰对象(基础核心产品)
java
// 基础美式咖啡(核心被装饰对象)
public class AmericanoCoffee implements Drink{
@Override
public double getPrice() {
return 15.0;
}
@Override
public String getDesc() {
return "基础美式咖啡";
}
}
③ 抽象装饰器父类
java
/**
* 抽象装饰器:统一装饰规范,持有被装饰对象
*/
public abstract class DrinkDecorator implements Drink{
// 组合被装饰的饮品对象
protected Drink drink;
public DrinkDecorator(Drink drink) {
this.drink = drink;
}
}
④ 具体装饰器(各类功能增强)
java
// 加糖装饰器
public class SugarDecorator extends DrinkDecorator{
public SugarDecorator(Drink drink) {
super(drink);
}
@Override
public double getPrice() {
return drink.getPrice() + 2.0;
}
@Override
public String getDesc() {
return drink.getDesc() + "、加糖";
}
}
// 加牛奶装饰器
public class MilkDecorator extends DrinkDecorator{
public MilkDecorator(Drink drink) {
super(drink);
}
@Override
public double getPrice() {
return drink.getPrice() + 5.0;
}
@Override
public String getDesc() {
return drink.getDesc() + "、加牛奶";
}
}
⑤ 客户端多层装饰测试
java
public class DecoratorClient {
public static void main(String[] args) {
// 基础咖啡
Drink simpleCoffee = new AmericanoCoffee();
System.out.println(simpleCoffee.getDesc() + ",价格:" + simpleCoffee.getPrice());
// 多层动态装饰:基础咖啡+加糖+加牛奶
Drink luxuryCoffee = new MilkDecorator(new SugarDecorator(new AmericanoCoffee()));
System.out.println(luxuryCoffee.getDesc() + ",价格:" + luxuryCoffee.getPrice());
}
}
3.4 核心优缺点深度解析
✅ 核心优点
-
动态灵活扩展:可按需多层叠加功能,运行时动态增减,远超静态继承
-
开闭原则完美落地:新增扩展功能只需新增装饰器,不修改原有核心代码
-
职责分离:核心功能与扩展功能解耦,代码分层清晰
-
杜绝类爆炸:无需为每种功能组合新建子类,大幅精简代码
❌ 核心缺点
-
多层嵌套复杂:过多装饰层嵌套后,代码可读性下降,排查问题繁琐
-
产生大量小装饰类:每个扩展功能对应一个装饰器类,类数量小幅增多
3.5 工程落地高频场景
-
JDK原生经典场景:Java IO流(FileReader→BufferedReader多层装饰)
-
框架场景:Spring Cache缓存包装、请求响应多层包装、权限功能叠加增强
-
业务场景:订单功能叠加(加积分、加优惠券、加物流)、文件操作增强
3.6 面试高频问答(必背)
问题1:装饰器和继承的核心区别?
继承是静态扩展,编译期固定、无法动态修改、易类爆炸;
装饰器是动态扩展,运行时灵活叠加、多层组合、无侵入扩展,更符合开闭与组合复用思想。
问题2:装饰器和代理模式的核心区别?(高频易错)
装饰器侧重功能叠加增强,同类型嵌套,目的是丰富对象能力;
代理模式侧重对象访问管控,拦截请求、前后增强,目的是控制对象访问权限、流程,不侧重功能叠加。
3.7 面试满分终极总结(万能话术)
装饰器模式是动态扩展对象功能的核心结构型模式,通过同类型嵌套包装、组合复用的方式,在不修改原有对象源码的前提下,实现功能的多层、动态叠加。彻底解决了继承扩展僵硬、类爆炸的问题,完美遵循开闭原则与单一职责,核心用于对象功能的灵活增强,典型落地场景为Java IO流包装。
4. 组合模式(Composite)------ 树形结构统一处理
4.1 核心定义与底层思想
官方标准定义 :将对象组合成树形层级结构,统一定义叶子节点与容器节点的行为,使客户端可以一致处理单个对象(叶子)和对象组合(容器),无需区分节点类型。
通俗解读:统一树形结构中"单个节点"和"分支节点"的操作,遍历、查询、处理时不用区分类型,一套逻辑统一处理所有节点。
核心底层思想 :部分与整体统一、树形递归处理、一致化操作
核心解决痛点:
-
树形结构需要区分叶子/容器节点编写两套逻辑,代码冗余繁琐
-
层级嵌套树形结构,递归处理复杂、代码耦合高
-
新增节点类型需要修改核心处理逻辑,扩展性差
4.2 核心组成组件
-
抽象组件(Component):统一树形节点顶层规范,定义通用行为
-
叶子节点(Leaf):最小单元节点,无下级子节点
-
容器节点(Composite):分支节点,可包含子节点(叶子/容器),支持增删子节点
4.3 完整实战可运行代码(面试手撕版)
业务场景:系统菜单树形结构,根菜单(容器)、子菜单(容器)、按钮权限(叶子),统一实现菜单展示、层级遍历。
① 统一抽象节点接口
java
/**
* 树形节点统一抽象组件:菜单通用规范
*/
public interface MenuComponent {
// 展示菜单信息
void showMenu(int level);
}
② 叶子节点(最小权限按钮)
java
// 叶子节点:按钮权限(无下级节点)
public class ButtonMenu implements MenuComponent{
private String menuName;
public ButtonMenu(String menuName) {
this.menuName = menuName;
}
@Override
public void showMenu(int level) {
System.out.println(" ".repeat(level) + "叶子权限:" + menuName);
}
}
③ 容器节点(菜单容器)
java
import java.util.ArrayList;
import java.util.List;
// 容器节点:菜单目录(可包含子节点)
public class MenuContainer implements MenuComponent{
private String menuName;
// 存储所有子节点(叶子/容器均可)
private List<MenuComponent> childMenu = new ArrayList<>();
public MenuContainer(String menuName) {
this.menuName = menuName;
}
// 新增子节点
public void addMenu(MenuComponent menu){
childMenu.add(menu);
}
@Override
public void showMenu(int level) {
System.out.println(" ".repeat(level) + "菜单目录:" + menuName);
// 递归遍历所有子节点,统一处理
for (MenuComponent menu : childMenu) {
menu.showMenu(level + 2);
}
}
}
④ 客户端测试调用
java
public class CompositeClient {
public static void main(String[] args) {
// 构建树形菜单结构
MenuContainer root = new MenuContainer("系统根菜单");
MenuContainer userMenu = new MenuContainer("用户管理");
MenuContainer orderMenu = new MenuContainer("订单管理");
// 新增叶子权限节点
userMenu.addMenu(new ButtonMenu("用户新增"));
userMenu.addMenu(new ButtonMenu("用户删除"));
orderMenu.addMenu(new ButtonMenu("订单查询"));
// 组装层级
root.addMenu(userMenu);
root.addMenu(orderMenu);
// 统一递归遍历展示
root.showMenu(0);
}
}
4.4 核心优缺点深度解析
✅ 核心优点
-
统一操作逻辑:客户端无需区分叶子与容器节点,一套逻辑处理所有树形节点
-
树形扩展灵活:新增节点类型只需实现抽象接口,不改动原有核心逻辑
-
递归处理简洁:天然适配树形层级递归遍历,代码极简优雅
-
符合开闭原则:新增节点、新增层级无需修改业务代码
❌ 核心缺点
-
层级约束弱:无法严格约束节点层级关系,可能出现非法嵌套
-
接口冗余:叶子节点无需增删子节点方法,被迫继承通用接口,存在接口冗余
4.5 工程落地高频场景
-
系统架构场景:后台菜单树形结构、部门组织架构、权限层级体系
-
文件系统场景:电脑文件目录、文件夹+文件统一遍历处理
-
业务场景:商品分类树形、审批流程层级、树形评论体系
4.6 面试满分终极总结(万能话术)
组合模式是专门适配树形层级结构的结构型模式,核心通过抽象统一叶子节点与容器节点的行为,让客户端可以一致处理单个对象与组合对象,无需区分节点类型。天然支持递归遍历,极大简化树形结构的开发复杂度,扩展灵活、代码简洁,广泛应用于菜单、组织架构、文件目录等所有树形层级业务场景。
5. 外观模式(Facade 门面)------ 统一入口简化调用
5.1 核心定义与底层思想
官方标准定义:为复杂的子系统提供一个统一的高层访问入口,封装子系统内部复杂的多模块交互逻辑,简化客户端调用,降低客户端与子系统的耦合度。
通俗解读:做一个统一门面入口,屏蔽底层多个复杂子系统的调用细节,客户端只对接门面,不用关心底层复杂逻辑。
核心底层思想 :分层隔离、统一入口、简化调用、屏蔽复杂
核心解决痛点:
-
客户端需要依赖多个子系统,调用逻辑繁琐、耦合极高
-
子系统内部交互复杂,客户端需要感知底层细节,维护成本高
-
多层调用导致代码冗余,业务入口不统一
5.2 核心组成组件
-
门面角色(Facade):核心统一入口,封装所有子系统调用逻辑,对外提供极简方法
-
子系统角色:多个独立复杂业务模块,内部逻辑复杂、互相依赖
-
客户端角色:仅依赖门面入口,不直接对接子系统
5.3 完整实战可运行代码(面试手撕版)
业务场景:用户下单流程,底层包含库存校验、支付扣款、物流生成、消息通知多个子系统,通过门面模式统一封装下单入口,简化客户端调用。
① 多个底层复杂子系统
java
// 子系统1:库存模块
public class StockSystem {
public boolean checkStock(){
System.out.println("库存校验:商品库存充足");
return true;
}
}
// 子系统2:支付模块
public class PaySystem {
public boolean payMoney(){
System.out.println("支付模块:扣款成功");
return true;
}
}
// 子系统3:物流模块
public class LogisticsSystem {
public void createLogistics(){
System.out.println("物流模块:生成物流单号,安排发货");
}
}
// 子系统4:消息通知模块
public class MessageSystem {
public void sendNotice(){
System.out.println("消息通知:发送下单成功短信");
}
}
② 统一门面入口类
java
/**
* 下单门面:统一封装所有子系统逻辑
* 客户端仅调用该类,无需感知底层多个模块
*/
public class OrderFacade {
// 组合所有子系统
private StockSystem stock = new StockSystem();
private PaySystem pay = new PaySystem();
private LogisticsSystem logistics = new LogisticsSystem();
private MessageSystem message = new MessageSystem();
// 统一封装完整下单流程
public void createOrder(){
stock.checkStock();
pay.payMoney();
logistics.createLogistics();
message.sendNotice();
System.out.println("===== 下单流程全部完成 =====");
}
}
③ 客户端极简调用
java
public class FacadeClient {
public static void main(String[] args) {
// 客户端仅对接门面,一行代码完成下单
OrderFacade orderFacade = new OrderFacade();
orderFacade.createOrder();
}
}
5.4 核心优缺点深度解析
✅ 核心优点
-
极致解耦:客户端与底层子系统彻底解耦,屏蔽底层复杂细节
-
调用极简:复杂多模块流程封装为统一入口,客户端一行代码调用
-
分层清晰:实现系统层级隔离,符合迪米特最少知道原则
-
维护性强:子系统内部修改不影响客户端,仅需调整门面封装逻辑
❌ 核心缺点
-
门面类易臃肿:过多流程封装会导致门面类代码冗余、职责过重
-
违背开闭风险:流程变更频繁时,需频繁修改门面类
5.5 工程落地高频场景
-
框架源码场景:Spring事务门面、Tomcat请求处理门面、网关统一入口
-
业务开发场景:下单、注册、退款等复杂串联流程统一封装
-
工具封装场景:第三方复杂SDK统一封装、多工具类整合入口
5.6 面试高频问答(必背)
问题:门面模式和中介者模式的区别?(高频易错)
门面模式是单向调用,客户端调用门面、门面调用子系统,解决多子系统调用复杂问题;
中介者模式是多对多双向通信,解决多个对象互相依赖、耦合混乱的问题。
5.7 面试满分终极总结(万能话术)
门面模式是简化系统调用、实现层级解耦的核心结构型模式,核心通过统一门面入口封装多个复杂子系统的串联逻辑,屏蔽底层实现细节,让客户端极简调用。严格遵循迪米特法则,大幅降低系统耦合度、简化业务调用流程,是复杂业务流程封装、系统分层设计的最优实践。
6. 享元模式(Flyweight)------ 内存对象复用优化
6.1 核心定义与底层思想
官方标准定义 :运用共享技术,高效复用大量细粒度相似对象,将对象内部不变状态 全局共享,外部可变状态动态传入,极大减少对象创建数量、降低内存占用。
通俗解读:重复对象不重复创建,缓存共享不变的公共属性,动态替换可变属性,实现对象复用、节省内存。
核心底层思想 :状态拆分、对象缓存、全局复用、内存优化
核心解决痛点:
-
系统存在大量相似细粒度对象,重复创建销毁导致内存占用过高、GC频繁
-
大量冗余对象,资源浪费严重、系统性能低下
6.2 核心状态拆分(面试必考)
-
内部状态 :对象不变、可共享的属性,全局唯一缓存,所有请求共用
-
外部状态 :对象可变、不共享的属性,每次使用动态传入,独立可变
6.3 完整实战可运行代码(面试手撕版)
业务场景:网站公共角色权限,内部状态(角色名称、权限范围-不变共享),外部状态(用户ID、操作时间-动态可变),实现角色对象全局复用。
① 抽象享元角色
java
/**
* 享元抽象类:统一角色行为
*/
public abstract class RoleFlyweight {
// 内部状态:不变、可共享(角色权限)
protected String roleName;
public RoleFlyweight(String roleName) {
this.roleName = roleName;
}
// 传入外部可变状态,执行业务逻辑
public abstract void showRoleInfo(String userId,String operateTime);
}
② 具体享元角色(可共享对象)
java
// 具体角色享元对象
public class ConcreteRoleFlyweight extends RoleFlyweight{
public ConcreteRoleFlyweight(String roleName) {
super(roleName);
}
@Override
public void showRoleInfo(String userId, String operateTime) {
System.out.println("当前角色:" + roleName + ",操作用户:" + userId + ",操作时间:" + operateTime);
}
}
③ 享元工厂(核心缓存池)
java
import java.util.HashMap;
import java.util.Map;
/**
* 享元工厂:维护全局对象缓存池,实现复用
*/
public class RoleFlyweightFactory {
// 全局缓存池:存储共享对象
private static final Map<String,RoleFlyweight> ROLE_CACHE = new HashMap<>();
// 获取享元对象:有则复用,无则创建
public static RoleFlyweight getRole(String roleName){
if(!ROLE_CACHE.containsKey(roleName)){
ROLE_CACHE.put(roleName,new ConcreteRoleFlyweight(roleName));
System.out.println("新建角色对象:" + roleName);
}else{
System.out.println("复用已有角色对象:" + roleName);
}
return ROLE_CACHE.get(roleName);
}
// 获取缓存对象数量
public static int getCacheSize(){
return ROLE_CACHE.size();
}
}
④ 客户端测试(复用效果演示)
java
public class FlyweightClient {
public static void main(String[] args) {
// 多次获取相同角色,复用对象
RoleFlyweight admin1 = RoleFlyweightFactory.getRole("超级管理员");
admin1.showRoleInfo("user001","2026-07-24");
RoleFlyweight admin2 = RoleFlyweightFactory.getRole("超级管理员");
admin2.showRoleInfo("user002","2026-07-24");
RoleFlyweight user = RoleFlyweightFactory.getRole("普通用户");
user.showRoleInfo("user003","2026-07-24");
System.out.println("全局缓存对象总数:" + RoleFlyweightFactory.getCacheSize());
}
}
6.4 核心优缺点深度解析
✅ 核心优点
-
极致节省内存:共享不变对象,大幅减少实例创建数量,降低JVM内存占用、减少GC
-
性能大幅提升:规避重复对象创建销毁开销,响应速度更快
-
统一管控:通过工厂缓存池统一管理全局对象,资源可控
❌ 核心缺点
-
代码复杂度提升:需要拆分内外状态、维护缓存池,逻辑更繁琐
-
内存常驻风险:共享对象长期常驻内存,少量对象复用场景无优势
6.5 工程落地高频场景
-
JDK原生场景:String字符串常量池、Integer缓存池、基本类型包装类缓存
-
中间件场景:线程池、数据库连接池、Redis热点数据缓存
-
业务场景:系统角色、字典、常量、模板类等高频复用细粒度对象
6.6 面试满分终极总结(万能话术)
享元模式是专门优化内存性能的结构型模式,核心通过拆分对象内外状态,缓存共享内部不变状态,动态传入外部可变状态,实现大量细粒度相似对象的复用。大幅减少对象创建数量、降低内存占用与GC频率,是系统性能优化的核心手段,广泛应用于各类缓存池、常量池、资源池场景。
7. 代理模式(Proxy)------ 对象访问管控增强
7.1 核心定义与底层思想
官方标准定义:为目标对象提供一个代理占位,由代理对象控制原对象的访问、调用流程,在不修改原对象源码的前提下,实现前置拦截、后置增强、流程管控。
通俗解读:找一个"代理替身"接管原对象的访问,在原方法执行前后做权限、日志、事务、缓存等增强操作,核心业务不变。
核心底层思想 :访问拦截、无侵入增强、流程管控、解耦增强逻辑
核心解决痛点:
-
核心业务与非核心增强逻辑(日志、权限、事务)耦合严重
-
需要统一管控对象访问权限、拦截非法请求
-
远程调用、延迟加载等特殊场景需要中间代理转发
7.2 三大实现方式(面试重中之重)
-
静态代理:手动编写代理类,实现统一接口,一对一代理,代码冗余、扩展性差
-
JDK动态代理 :基于接口动态生成代理类,无需手动编写,无接口无法使用
-
CGLIB动态代理 :基于子类继承实现,无需接口,可代理普通类,底层ASM字节码增强
7.3 核心代码实战(JDK动态代理|工程主流)
业务场景:订单服务代理,在订单查询前后实现日志记录、权限校验增强。
① 业务顶层接口
java
public interface OrderService {
// 查询订单
void getOrderInfo(String orderId);
}
② 真实目标对象(被代理类)
java
// 真实订单业务实现
public class OrderServiceImpl implements OrderService{
@Override
public void getOrderInfo(String orderId) {
System.out.println("核心业务:查询订单【" + orderId + "】详情");
}
}
③ JDK动态代理工厂类
四、第三大类:行为型模式(11种|完整版|面试满分体系)
大类总纲回顾 :行为型模式核心是规范多对象协作、封装业务行为与流程,解决系统中对象交互混乱、分支代码臃肿、流程僵硬、状态不可控、事件耦合严重等问题。
核心定位 :不关注对象创建、不修改对象结构,只管控对象运行时的动态行为、通信规则、流程流转。
设计原则落地 :高度贴合开闭原则、单一职责、迪米特法则,彻底解耦业务行为与业务主体。
11种模式精准分工总览:
流程管控:模板方法、责任链
算法切换:策略模式
状态流转:状态模式
事件驱动:观察者模式
请求封装:命令模式、迭代器模式
多对象解耦:中介者模式
状态备份:备忘录模式
结构操作分离:访问者模式
自定义语法解析:解释器模式
1. 模板方法模式(Template Method)------ 固定骨架、可变步骤
1.1 核心定义与底层思想
官方定义:定义一个算法的通用骨架流程,将算法中的固定步骤在父类实现,可变步骤定义为抽象方法交由子类实现,让子类在不改变整体流程结构的前提下,自定义差异化步骤。
通俗解读:流程骨架统一固定,通用步骤父类搞定,个性化步骤子类重写,统一流程、灵活差异。
核心底层思想 :通用流程复用、差异化扩展、骨架固定、步骤可变
核心解决痛点:
-
多个子类存在重复通用流程,代码冗余严重
-
业务流程不统一,各子类实现杂乱无章、维护困难
-
流程迭代需要修改多个子类,改造成本高
1.2 核心组成组件
-
抽象模板类:定义完整算法骨架(final固定流程),包含通用实现方法、抽象差异化方法、钩子方法
-
具体实现子类:重写抽象差异化方法,实现个性化业务逻辑
-
钩子方法:可选重写的空方法,用于动态控制流程执行,灵活适配业务
1.3 完整实战可运行代码(面试手撕版)
业务场景:后端定时任务通用执行流程,固定流程:初始化任务→执行任务→日志记录→收尾销毁,不同定时任务自定义执行逻辑。
① 抽象任务模板类(固定骨架)
java
/**
* 定时任务通用模板类
* 固定整体执行骨架,子类仅实现差异化任务逻辑
*/
public abstract class TaskTemplate {
// 固定算法骨架:final禁止子类重写流程,保证流程统一
public final void executeTask(){
initTask();
doTask();
recordLog();
finishTask();
}
// 通用步骤:任务初始化,所有任务共用
private void initTask(){
System.out.println("【通用流程】定时任务初始化完成");
}
// 抽象差异化步骤:子类必须实现自定义任务逻辑
protected abstract void doTask();
// 通用步骤:日志记录,所有任务共用
private void recordLog(){
System.out.println("【通用流程】任务执行日志记录完成");
}
// 钩子方法:可选重写,任务收尾逻辑
protected void finishTask(){}
}
② 具体子类实现(差异化任务)
java
// 订单超时处理任务
public class OrderTimeoutTask extends TaskTemplate{
@Override
protected void doTask() {
System.out.println("【差异化任务】处理超时未支付订单,关闭订单、释放库存");
}
// 重写钩子方法,自定义收尾逻辑
@Override
protected void finishTask() {
System.out.println("【任务收尾】推送订单超时提醒消息");
}
}
// 每日数据统计任务
public class DataStatisticsTask extends TaskTemplate{
@Override
protected void doTask() {
System.out.println("【差异化任务】统计每日订单、用户、交易数据,生成报表");
}
}
③ 客户端测试调用
java
public class TemplateClient {
public static void main(String[] args) {
// 执行订单超时任务
new OrderTimeoutTask().executeTask();
System.out.println("===== 任务分割 =====");
// 执行数据统计任务
new DataStatisticsTask().executeTask();
}
}
1.4 核心优缺点深度解析
✅ 核心优点
-
极致代码复用:通用流程统一封装父类,彻底消除重复代码
-
流程统一规范:final固定核心骨架,保证所有子类业务流程一致
-
扩展灵活:新增业务只需新增子类,重写差异化方法,符合开闭原则
-
职责清晰:父类管通用流程,子类管差异化逻辑,单一职责落地
❌ 核心缺点
-
继承耦合:基于继承实现,父类流程变更会影响所有子类
-
类数量膨胀:每一个差异化场景都需要新增一个子类
-
流程固化:核心骨架固定,无法动态调整整体流程
1.5 工程落地高频场景
-
框架源码场景:Spring JdbcTemplate、事务模板、SpringBoot启动流程、IO读写模板
-
业务场景:定时任务、文件解析、报表生成、接口统一校验流程
-
工具场景:通用数据处理、消息解析、流程审批通用骨架
1.6 面试高频问答
问题1:模板方法为什么要把骨架方法加final?
防止子类重写整体业务流程,破坏统一规范,保证算法骨架全局唯一,只允许子类扩展差异化步骤,不允许修改整体流程。
问题2:钩子方法的作用?
钩子方法是可选重写的空方法,用于在固定流程中预留扩展口子,让子类可以按需增强收尾、前置校验等个性化逻辑,提升模板灵活性。
1.7 面试满分终极总结
模板方法模式是基于继承的流程复用型行为模式,核心通过父类固定通用算法骨架,将差异化步骤交由子类实现,兼顾流程统一性和业务灵活性。极大减少重复代码、规范业务流程,是框架底层通用模板的核心实现思想,严格遵循复用优先、开闭的设计理念。
2. 策略模式(Strategy)------ 平等算法动态切换
2.1 核心定义与底层思想
官方定义:定义一系列独立的算法策略,将每个算法封装为独立类,让算法可以互相替换,使得算法的变化独立于使用算法的客户端。
通俗解读:把不同的业务算法、处理规则单独封装,需要哪种就切换哪种,彻底消灭if-else分支判断。
核心底层思想 :算法封装、平等替换、动态切换、解耦算法与业务
核心解决痛点:
-
多算法场景if-else/switch分支臃肿、代码难维护
-
新增算法需要修改核心业务代码,违背开闭原则
-
算法与业务逻辑耦合,复用性差
2.2 核心组成组件
-
策略接口:统一所有算法的通用规范
-
具体策略实现类:各类差异化算法独立实现
-
上下文环境类:持有策略引用,对外统一调用入口,负责动态切换策略
2.3 完整实战可运行代码(面试手撕版)
业务场景:订单支付场景,支持微信支付、支付宝支付、银行卡支付三种策略,动态切换支付方式。
① 统一支付策略接口
java
/**
* 支付策略统一接口
*/
public interface PayStrategy {
// 统一支付方法
void pay(double money);
}
② 具体支付策略实现
java
// 支付宝支付策略
public class AlipayStrategy implements PayStrategy{
@Override
public void pay(double money) {
System.out.println("支付宝支付成功,支付金额:" + money + "元");
}
}
// 微信支付策略
public class WechatPayStrategy implements PayStrategy{
@Override
public void pay(double money) {
System.out.println("微信支付成功,支付金额:" + money + "元");
}
}
// 银行卡支付策略
public class BankPayStrategy implements PayStrategy{
@Override
public void pay(double money) {
System.out.println("银行卡支付成功,支付金额:" + money + "元");
}
}
③ 策略上下文切换类
java
/**
* 策略上下文:负责持有、切换策略,对外提供统一调用
*/
public class PayContext {
// 持有当前支付策略
private PayStrategy payStrategy;
// 动态设置策略
public void setPayStrategy(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
// 统一执行支付
public void executePay(double money){
payStrategy.pay(money);
}
}
④ 客户端测试调用
java
public class StrategyClient {
public static void main(String[] args) {
PayContext context = new PayContext();
// 切换支付宝支付
context.setPayStrategy(new AlipayStrategy());
context.executePay(99.9);
// 切换微信支付
context.setPayStrategy(new WechatPayStrategy());
context.executePay(199.9);
}
}
2.4 核心优缺点深度解析
✅ 核心优点
-
消灭臃肿分支:彻底替代if-else多分支判断,代码简洁优雅
-
完美契合开闭:新增支付/算法策略只需新增实现类,不修改原有代码
-
算法独立复用:各算法独立封装,可单独复用、单元测试
-
动态灵活切换:运行时动态替换策略,适配多样化业务场景
❌ 核心缺点
-
类数量增多:每一种算法对应一个实现类,策略过多会导致类泛滥
-
客户端感知策略:客户端需要了解所有策略差异,才能选择对应策略
2.5 工程落地高频场景
-
业务场景:多支付渠道、多优惠算法、多排序规则、多文件解析方式
-
框架场景:Spring资源加载策略、限流策略、线程池拒绝策略
-
工具场景:加密算法、校验规则、数据脱敏策略切换
2.6 高频易混区分:策略 vs 状态模式
策略模式 :多个算法平等独立、无先后顺序,人为主动切换,无状态流转;
状态模式 :多个状态有固定流转顺序,由业务场景自动驱动切换,被动变更。
2.7 面试满分终极总结
策略模式是解耦多算法分支的核心行为模式,通过封装平等独立的算法策略,由上下文统一调度,实现运行时动态切换算法。彻底消灭冗余if-else代码,符合开闭与单一职责原则,是业务分支代码重构、多规则动态适配的最优方案。
3. 状态模式(State)------ 有序状态自动流转
3.1 核心定义与底层思想
官方定义:允许一个对象在其内部状态改变时,自动改变它的行为,对象看起来似乎修改了它的类,将不同状态的行为独立封装,实现状态与行为的绑定。
通俗解读:把对象的每一种状态单独封装,状态切换自动触发对应行为,无需大量if-else判断状态执行逻辑。
核心底层思想 :状态封装、行为绑定、自动流转、状态驱动行为
核心解决痛点:
-
多状态流转场景,大量if-else判断状态,代码臃肿混乱
-
状态与行为耦合严重,新增状态需修改核心代码
-
状态流转规则分散,维护困难、易出现状态异常
3.2 核心组成组件
-
抽象状态接口:统一所有状态的行为规范,定义状态切换方法
-
具体状态类:每个状态独立封装,实现当前状态的专属行为与流转规则
-
上下文环境类:持有当前状态、维护主体对象,负责触发状态切换
3.3 完整实战可运行代码(面试手撕版)
业务场景:订单状态流转,核心状态:待支付、已支付、已发货、已完成,实现状态自动流转与对应行为。
① 订单状态统一接口
java
/**
* 订单状态统一接口
*/
public interface OrderState {
// 状态对应的业务行为
void handle();
// 状态流转切换
void changeState(OrderContext context);
}
② 具体状态实现类
java
// 待支付状态
public class UnPayState implements OrderState{
@Override
public void handle() {
System.out.println("订单待支付:展示支付入口、倒计时超时");
}
@Override
public void changeState(OrderContext context) {
// 支付完成,切换为已支付状态
context.setOrderState(new PaidState());
}
}
// 已支付状态
public class PaidState implements OrderState{
@Override
public void handle() {
System.out.println("订单已支付:校验库存、触发发货流程");
}
@Override
public void changeState(OrderContext context) {
// 发货完成,切换为已发货状态
context.setOrderState(new ShippedState());
}
}
// 已发货状态
public class ShippedState implements OrderState{
@Override
public void handle() {
System.out.println("订单已发货:更新物流信息、推送物流通知");
}
@Override
public void changeState(OrderContext context) {
// 确认收货,切换为已完成状态
context.setOrderState(new FinishState());
}
}
// 已完成状态
public class FinishState implements OrderState{
@Override
public void handle() {
System.out.println("订单已完成:生成订单报表、开启售后入口");
}
@Override
public void changeState(OrderContext context) {
// 最终状态,无后续流转
System.out.println("订单已完结,状态不可变更");
}
}
③ 订单上下文类
java
/**
* 订单上下文:维护当前状态、驱动状态流转
*/
public class OrderContext {
// 默认初始状态:待支付
private OrderState orderState = new UnPayState();
public void setOrderState(OrderState orderState) {
this.orderState = orderState;
}
// 执行当前状态行为
public void doHandle(){
orderState.handle();
}
// 触发状态流转
public void nextState(){
orderState.changeState(this);
}
}
④ 客户端测试调用
java
public class StateClient {
public static void main(String[] args) {
OrderContext order = new OrderContext();
// 初始状态行为
order.doHandle();
// 逐级自动流转
order.nextState();
order.doHandle();
order.nextState();
order.doHandle();
order.nextState();
order.doHandle();
order.nextState();
}
}
3.4 核心优缺点深度解析
✅ 核心优点
-
彻底消灭状态分支:状态行为独立封装,无需大量if-else判断
-
开闭原则落地:新增订单状态只需新增状态类,不修改原有代码
-
状态流转可控:流转规则统一收敛在状态类中,逻辑清晰、便于维护
-
职责单一:每个状态只负责自身行为与流转,职责高度内聚
❌ 核心缺点
-
类数量激增:状态过多时,会产生大量状态实现类
-
流转逻辑分散:完整流转规则分散在多个状态类中,全局查看不直观
3.5 工程落地高频场景
-
核心业务场景:订单/支付/审批/工单状态流转、请假流程、售后流程
-
其他场景:游戏角色状态、设备启停状态、任务执行状态
3.6 面试满分终极总结
状态模式是处理有序状态流转的专属行为模式,核心将每一种状态的行为和流转规则独立封装,由上下文驱动自动状态切换,彻底解决多状态场景分支代码臃肿、逻辑混乱的问题。专注状态驱动行为,适配所有有序流转的业务场景,代码扩展性、可维护性极强。
4. 观察者模式(Observer)------ 一对多发布订阅
4.1 核心定义与底层思想
官方定义:定义一对多的依赖关系,让一个主体对象状态发生改变时,所有依赖它的观察者对象自动收到通知并更新,实现事件驱动。
通俗解读:一个发布者、多个订阅者,发布者状态变更,自动推送消息给所有订阅者,无需逐个通知。
核心底层思想 :一对多依赖、事件驱动、发布订阅、解耦主体与观察者
核心解决痛点:
-
主体变更需要逐个调用多个订阅者代码,耦合极高
-
新增订阅者需要修改主体代码,违背开闭原则
-
事件通知逻辑冗余,无法统一管理订阅关系
4.2 核心组成组件
-
抽象主题(发布者):维护观察者列表,提供订阅、取消订阅、通知方法
-
具体主题:主体状态变更,触发全局通知
-
抽象观察者:统一更新回调接口
-
具体观察者:接收通知,执行自定义回调逻辑
4.3 完整实战可运行代码(面试手撕版)
业务场景:系统消息发布,后台公告更新(发布者),用户、管理员、日志系统(订阅者)自动接收通知。
① 抽象观察者接口
java
/**
* 观察者统一接口
*/
public interface Observer {
// 接收通知、更新逻辑
void update(String message);
}
② 具体观察者实现
java
// 普通用户观察者
public class UserObserver implements Observer{
@Override
public void update(String message) {
System.out.println("普通用户接收公告:" + message);
}
}
// 管理员观察者
public class AdminObserver implements Observer{
@Override
public void update(String message) {
System.out.println("管理员接收公告:" + message);
}
}
// 日志观察者
public class LogObserver implements Observer{
@Override
public void update(String message) {
System.out.println("日志系统记录公告:" + message);
}
}
③ 抽象主题(发布者)
java
import java.util.ArrayList;
import java.util.List;
/**
* 主题发布者抽象类
*/
public abstract class Subject {
// 维护所有订阅的观察者
protected List<Observer> observerList = new ArrayList<>();
// 订阅观察者
public void addObserver(Observer observer){
observerList.add(observer);
}
// 取消订阅
public void removeObserver(Observer observer){
observerList.remove(observer);
}
// 统一通知所有观察者
public abstract void notifyAllObserver(String message);
}
④ 具体主题实现
java
// 系统公告发布主题
public class NoticeSubject extends Subject{
@Override
public void notifyAllObserver(String message) {
// 遍历所有观察者,推送通知
for (Observer observer : observerList) {
observer.update(message);
}
}
}
⑤ 客户端测试调用
java
public class ObserverClient {
public static void main(String[] args) {
// 创建发布者
NoticeSubject subject = new NoticeSubject();
// 注册多个观察者
subject.addObserver(new UserObserver());
subject.addObserver(new AdminObserver());
subject.addObserver(new LogObserver());
// 发布公告,自动通知所有订阅者
subject.notifyAllObserver("系统将于今晚23点进行版本升级维护");
}
}
4.4 核心优缺点深度解析
✅ 核心优点
-
彻底解耦:发布者与订阅者互不依赖,通过订阅关系关联
-
开闭原则:新增观察者无需修改发布者代码,直接订阅即可
-
一对多联动:一次状态变更,自动触发所有订阅者更新,联动高效
❌ 核心缺点
-
通知顺序无序:默认无法保证观察者执行顺序
-
内存泄漏风险:观察者未及时取消订阅,会导致对象常驻内存
-
链式通知风险:观察者更新逻辑触发新的通知,易导致循环调用
4.5 工程落地高频场景
-
框架源码场景:Spring事件驱动、ApplicationEvent、Guava事件总线
-
业务场景:消息通知、公告推送、数据变更监听、订单状态推送
-
技术场景:MQ发布订阅、前端事件监听、回调通知机制
4.6 面试满分终极总结
观察者模式是一对多事件驱动的核心行为模式,通过订阅机制解耦发布者与观察者,实现主体状态变更自动通知所有订阅对象。遵循开闭与迪米特法则,是系统事件监听、消息推送、数据联动更新的核心实现方案,广泛应用于框架事件驱动与业务通知场景。
5. 责任链模式(Chain of Responsibility)------ 链式逐级处理请求
5.1 核心定义与底层思想
官方定义:将多个处理器对象串联成一条责任链路,请求从链路头部传入,逐级传递处理,每个处理器可选择自行处理请求、放行下一级、拦截终止链路,实现请求的链式分发。
通俗解读:多个校验/处理环节串成链条,请求逐级审核,能处理就执行,不能处理就转交下一级,可随时终止链路。
核心底层思想 :职责拆分、链式传递、逐级处理、动态拦截
核心解决痛点:
-
多级校验、多级处理逻辑嵌套臃肿,代码耦合严重
-
处理层级固定,无法动态增减处理节点、调整链路顺序
-
职责划分不清晰,新增处理规则需修改核心代码
5.2 核心组成组件
-
抽象处理器:统一处理规范,持有下一级处理器引用
-
具体处理器:实现专属处理逻辑,控制链路放行/终止
-
请求实体:封装需要处理的请求参数
5.3 完整实战可运行代码(面试手撕版)
业务场景:用户注册请求校验链路,依次完成:参数非空校验、手机号格式校验、重复注册校验,逐级拦截放行。
① 请求实体类
java
// 注册请求参数
public class RegisterRequest {
private String username;
private String phone;
// 构造、get/set
public RegisterRequest(String username, String phone) {
this.username = username;
this.phone = phone;
}
public String getUsername() {
return username;
}
public String getPhone() {
return phone;
}
}
② 抽象处理器
java
/**
* 抽象责任处理器
*/
public abstract class Handler {
// 持有下一级处理器
protected Handler nextHandler;
// 设置下一级处理器
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
// 抽象处理方法
public abstract boolean handle(RegisterRequest request);
}
③ 各级具体处理器
java
// 1. 参数非空校验处理器
public class ParamNotNullHandler extends Handler{
@Override
public boolean handle(RegisterRequest request) {
if (request.getUsername() == null || request.getPhone() == null){
System.out.println("参数校验失败:用户名/手机号不能为空");
return false;
}
System.out.println("参数非空校验通过,进入下一级校验");
// 放行下一级
return nextHandler.handle(request);
}
}
// 2. 手机号格式校验处理器
public class PhoneFormatHandler extends Handler{
@Override
public boolean handle(RegisterRequest request) {
String phone = request.getPhone();
if (phone.length() != 11){
System.out.println("手机号格式校验失败:非11位有效手机号");
return false;
}
System.out.println("手机号格式校验通过,进入下一级校验");
return nextHandler.handle(request);
}
}
// 3. 重复注册校验处理器
public class RepeatRegisterHandler extends Handler{
@Override
public boolean handle(RegisterRequest request) {
// 模拟数据库查重
if ("admin".equals(request.getUsername())){
System.out.println("重复注册校验失败:用户名已存在");
return false;
}
System.out.println("重复注册校验通过,注册成功");
return true;
}
}
④ 客户端测试调用
java
public class ChainClient {
public static void main(String[] args) {
// 组装责任链
Handler paramHandler = new ParamNotNullHandler();
Handler phoneHandler = new PhoneFormatHandler();
Handler repeatHandler = new RepeatRegisterHandler();
paramHandler.setNextHandler(phoneHandler);
phoneHandler.setNextHandler(repeatHandler);
// 发起请求,链式处理
RegisterRequest request = new RegisterRequest("testUser","13800138000");
paramHandler.handle(request);
}
}
5.4 核心优缺点深度解析
✅ 核心优点
-
职责拆分清晰:每个处理器只负责单一校验/处理逻辑,单一职责
-
动态灵活扩展:可动态增减节点、调整链路顺序,无需修改原有代码
-
解耦彻底:请求发起方与处理方解耦,无需感知具体处理规则
-
链路可控:支持中途拦截、终止链路,适配多级校验场景
❌ 核心缺点
-
请求遍历损耗:链路过长时,请求逐级传递,性能略有损耗
-
循环依赖风险:手动组装链路不当,可能出现闭环死循环
5.5 工程落地高频场景
-
框架源码场景:Servlet Filter、Spring Interceptor、MyBatis插件链
-
业务场景:多级参数校验、权限校验、审批流程、风控拦截
-
技术场景:日志过滤器、请求拦截链路、规则校验链
5.6 面试满分终极总结
责任链模式通过串联多级处理器形成链式链路,实现请求逐级分发、逐级处理,将复杂的多级校验与处理逻辑拆分解耦。支持动态调整链路节点、灵活拦截请求,完美遵循开闭与单一职责原则,是多级流程校验、请求拦截架构的核心实现方案。
6. 命令模式(Command)------ 请求封装、可回溯可撤销
6.1 核心定义与底层思想
官方定义:将客户端发起的请求封装为独立的命令对象,将请求的发起者与执行者解耦,支持请求排队、撤销、重做、日志记录、延迟执行。
通俗解读:把每一次操作、每一个指令封装成对象,统一管理,可以排队执行、撤销回滚、记录操作日志。
核心底层思想 :请求对象化、调用解耦、可回溯、可批量调度
核心解决痛点:
-
请求发起与执行耦合,无法统一管控请求
-
不支持操作撤销、回滚、日志回放、延迟执行
-
批量操作、请求排队场景代码繁琐
6.2 核心组成组件
-
抽象命令接口:统一命令执行、撤销规范
-
具体命令对象:绑定接收者,实现具体执行、撤销逻辑
-
接收者:真正执行业务逻辑的实体对象
-
调用者:接收命令、调度执行、维护命令队列
6.3 完整实战可运行代码(面试手撕版)
业务场景:智能家居控制,实现开灯、关灯命令,支持执行与撤销操作。
① 接收者(真正执行业务)
java
// 灯光接收者:真正执行开关灯逻辑
public class LightReceiver {
public void openLight(){
System.out.println("灯光开启");
}
public void closeLight(){
System.out.println("灯光关闭");
}
}
② 抽象命令接口
java
// 命令统一接口
public interface Command {
// 执行命令
void execute();
// 撤销命令
void undo();
}
③ 具体命令实现
java
// 开灯命令
public class OpenLightCommand implements Command{
private LightReceiver light;
public OpenLightCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.openLight();
}
@Override
public void undo() {
light.closeLight();
}
}
// 关灯命令
public class CloseLightCommand implements Command{
private LightReceiver light;
public CloseLightCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.closeLight();
}
@Override
public void undo() {
light.openLight();
}
}
④ 命令调用者
java
// 命令调用者:统一调度命令
public class CommandInvoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
// 执行命令
public void doExecute(){
command.execute();
}
// 撤销命令
public void doUndo(){
command.undo();
}
}
⑤ 客户端测试调用
java
public class CommandClient {
public static void main(String[] args) {
// 创建接收者
LightReceiver light = new LightReceiver();
// 创建命令对象
Command openCmd = new OpenLightCommand(light);
Command closeCmd = new CloseLightCommand(light);
// 创建调用者
CommandInvoker invoker = new CommandInvoker();
// 执行开灯
invoker.setCommand(openCmd);
invoker.doExecute();
// 撤销开灯(关灯)
invoker.doUndo();
// 执行关灯
invoker.setCommand(closeCmd);
invoker.doExecute();
// 撤销关灯(开灯)
invoker.doUndo();
}
}
6.4 核心优缺点深度解析
✅ 核心优点
-
彻底解耦:请求发起者与执行者完全隔离,通过命令对象中转
-
支持回溯操作:天然支持撤销、重做、回滚,适配事务补偿场景
-
批量调度灵活:支持命令排队、延迟执行、批量执行、日志持久化
-
开闭原则:新增命令只需新增实现类,不修改原有代码
❌ 核心缺点
-
类数量泛滥:每一个操作对应一个命令类,场景复杂时类过多
-
流程繁琐:简单操作需要封装多层对象,增加代码复杂度
6.5 工程落地高频场景
-
业务场景:操作撤销重做、事务补偿、订单操作日志、审批撤回
-
技术场景:定时任务队列、GUI按钮操作、指令调度、消息回放
6.6 面试满分终极总结
命令模式核心是将请求封装为独立对象,解耦请求发起与业务执行,赋予请求撤销、重做、排队、延迟执行的能力。完美适配需要操作回溯、事务补偿、批量指令调度的业务场景,是业务操作可追溯、可回滚的核心设计方案。
7. 迭代器模式(Iterator)------ 统一容器遍历
7.1 核心定义与底层思想
官方定义 :迭代器模式属于行为型设计模式,提供一种统一、标准的方式来遍历聚合容器中的元素,在不暴露容器底层存储结构的前提下,对外屏蔽容器内部实现,让所有聚合对象使用一致的遍历逻辑。
通俗解读:无论是数组、List、Set、自定义复合容器,都可以用「hasNext+next」统一方式遍历,使用者无需关心底层是数组、链表还是树形结构,彻底统一遍历规范。
核心底层思想 :遍历逻辑与容器存储解耦、统一遍历行为、屏蔽底层异构差异、封装内部结构
核心解决痛点:
-
遍历方式不统一:不同容器(数组/链表/自定义容器)遍历语法不同,代码杂乱、复用性差
-
底层结构暴露:直接操作容器内部元素,外部可随意修改集合,破坏封装性,存在数据安全问题
-
自定义容器适配难:自研复合容器、树形容器无统一遍历规范,上层需要适配多套遍历逻辑
-
遍历逻辑冗余:每种容器重复编写遍历代码,冗余度高、维护成本大
7.2 核心组成组件(标准四组件)
-
抽象迭代器(Iterator):顶层统一接口,定义核心遍历方法:hasNext()判断是否有下一个元素、next()获取下一个元素,规范所有迭代器行为
-
具体迭代器(ConcreteIterator):实现抽象迭代器接口,维护遍历游标,完成对应容器的精准遍历,记录遍历进度
-
抽象聚合容器(Aggregate) :定义容器统一规范,提供
getIterator()方法,用于生成专属迭代器 -
具体聚合容器(ConcreteAggregate):实际存储数据的容器,实现迭代器获取方法,返回适配自身结构的迭代器实例
7.3 JDK原生源码落地(面试必问)
迭代器模式是JDK落地最彻底的设计模式之一,Java集合框架完全基于该模式实现:
-
java.util.Iterator:标准抽象迭代器接口,包含hasNext()、next()、remove()核心方法 -
java.util.Collection:抽象聚合顶层接口,定义iterator()方法获取迭代器 -
ArrayList/LinkedList/HashSet:具体聚合容器,各自实现专属迭代器,屏蔽数组、链表等底层差异
核心价值 :上层业务代码无需区分ArrayList还是LinkedList,统一使用迭代器遍历,完全符合依赖倒置、接口隔离、迪米特法则。
7.4 完整实战可运行代码(面试手撕版)
业务场景:自定义学生集合容器,封装存储结构,实现统一迭代遍历,外部无需感知底层数组存储逻辑。
① 学生实体类
java
/**
* 学生实体
*/
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
// Getter
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}
② 抽象迭代器接口
java
/**
* 统一迭代器接口
* @param <T> 泛型适配任意容器元素
*/
public interface Iterator<T> {
// 是否存在下一个元素
boolean hasNext();
// 获取下一个元素
T next();
}
③ 抽象聚合容器接口
java
/**
* 聚合容器顶层接口
* @param <T> 元素泛型
*/
public interface Aggregate<T> {
// 获取专属迭代器
Iterator<T> getIterator();
}
④ 具体迭代器实现(学生迭代器)
java
/**
* 学生集合专属迭代器
*/
public class StudentIterator implements Iterator<Student> {
// 待遍历的学生数组
private Student[] students;
// 遍历游标,记录当前位置
private int index = 0;
public StudentIterator(Student[] students) {
this.students = students;
}
@Override
public boolean hasNext() {
// 游标未越界且当前位置元素不为空
return index < students.length && students[index] != null;
}
@Override
public Student next() {
// 返回当前元素,游标后移
return students[index++];
}
}
⑤ 具体聚合容器实现(学生集合)
java
/**
* 自定义学生集合容器
* 底层基于数组存储,对外屏蔽存储细节
*/
public class StudentAggregate implements Aggregate<Student> {
// 底层存储数组
private Student[] students = new Student[10];
// 元素数量
private int size = 0;
// 添加学生元素
public void addStudent(Student student) {
students[size++] = student;
}
// 生成专属迭代器
@Override
public Iterator<Student> getIterator() {
return new StudentIterator(students);
}
}
⑥ 客户端测试调用
java
public class IteratorClient {
public static void main(String[] args) {
// 初始化自定义容器
StudentAggregate aggregate = new StudentAggregate();
aggregate.addStudent(new Student("张三", 18));
aggregate.addStudent(new Student("李四", 20));
aggregate.addStudent(new Student("王五", 19));
// 统一迭代遍历,无需感知底层数组结构
Iterator<Student> iterator = aggregate.getIterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println("学生姓名:" + student.getName() + ",年龄:" + student.getAge());
}
}
}
7.5 核心优缺点深度解析
✅ 核心优点
-
彻底解耦:遍历逻辑与容器存储结构分离,修改遍历规则无需改动容器源码,符合开闭原则
-
统一遍历规范:所有容器适配同一套遍历API,降低学习与适配成本,代码风格统一
-
封装性极强:屏蔽容器底层数组、链表、树形等实现细节,外部仅通过迭代器访问元素,保障数据安全
-
单一职责落地:容器只负责存储数据,迭代器只负责遍历逻辑,职责高度内聚
-
支持异构遍历:同一套迭代逻辑,可适配不同底层结构的容器,扩展性极强
❌ 核心缺点
-
类数量增多:每新增一个自定义容器,需要配套新增专属迭代器,轻微造成类膨胀
-
简单场景冗余:对于极简单的容器遍历,单独封装迭代器会增加代码层级与复杂度
-
不支持随机访问:迭代器仅支持顺序遍历,无法像数组一样通过下标精准获取元素
7.6 工程落地高频场景
-
JDK源码核心场景:所有集合类(List/Set/Map)迭代遍历、foreach循环底层实现(foreach本质是迭代器语法糖)
-
自定义容器场景:自研树形结构容器、分页数据容器、异构数据集合统一遍历
-
业务工具场景:批量数据处理、集合统一过滤、多数据源聚合遍历、数据导出迭代
-
框架底层场景:MyBatis结果集遍历、Spring容器Bean遍历、配置项迭代读取
7.7 面试高频问答(必背)
问题1:foreach循环与迭代器的关系?
foreach是JDK提供的迭代器语法糖,所有实现Iterable接口的容器都可以使用foreach遍历,编译后底层会自动生成迭代器遍历代码,简化手写迭代器的冗余代码,核心原理完全基于迭代器模式。
问题2:迭代器为什么能避免集合遍历并发修改异常?
ArrayList迭代器通过modCount版本号机制,遍历过程中若集合结构被修改(新增/删除元素),会快速抛出ConcurrentModificationException,提前规避并发遍历数据错乱问题,保障遍历安全性。
问题3:迭代器模式体现了哪些设计原则?
核心体现三大原则:单一职责 (容器存数据、迭代器做遍历)、接口隔离 (细粒度迭代遍历接口)、迪米特法则(屏蔽底层容器细节,最小化依赖),同时契合开闭原则。
7.8 面试满分终极总结
迭代器模式是解耦容器存储与遍历行为的经典行为型模式,核心通过抽象迭代器统一所有聚合容器的遍历规范,屏蔽数组、链表、树形结构等底层存储差异,实现遍历逻辑与容器结构完全解耦。既保证了代码统一性与封装安全性,又具备极强的扩展性,是Java集合框架、各类容器遍历的底层核心设计,日常开发高频使用、框架底层必备。
8. 中介者模式(Mediator)------ 多对多通信解耦
8.1 核心定义与底层思想
官方定义:定义一个专门的中介者对象,统一封装多个同事对象之间的交互逻辑,让原本多对多双向依赖的同事类不再直接相互调用,所有通信、交互、消息转发全部通过中介者完成,以此彻底解耦多对象复杂耦合关系。
通俗解读 :多个模块/对象互相通信会形成混乱的网状依赖,引入中间人统一传话,所有对象只和中介者交互,把多对多网状耦合 变成一对多星型解耦。
核心底层思想 :集中交互、隔离依赖、网状转星型、弱化双向耦合
核心解决痛点:
-
多业务对象两两直接依赖,依赖关系呈指数级暴涨,架构错综复杂
-
新增/删除业务对象时,需要修改所有关联对象的交互代码,严重违背开闭原则
-
交互逻辑分散在各个业务类中,逻辑混乱、统一维护困难、问题排查繁琐
-
对象职责混杂,既处理自身业务,又处理跨对象通信,违背单一职责原则
8.2 核心组成组件(标准四组件)
-
抽象中介者(Mediator):统一中介规范,定义同事对象通信、消息转发的抽象方法,约束所有具体中介者行为
-
具体中介者(ConcreteMediator):持有所有同事对象引用,封装全部交互规则,负责消息接收、转发、调度,是核心调度中枢
-
抽象同事类(Colleague):所有交互业务对象的顶层父类,持有中介者引用,统一同事对象基础行为
-
具体同事类(ConcreteColleague):具体业务对象,只与中介者通信,不直接关联其他同事类,专注自身核心业务
8.3 高频易混模式深度区分(面试必问)
中介者模式 vs 门面模式(最易混淆)
-
门面模式(外观模式) :单向调用、一对多 。客户端调用门面,门面调用底层子系统,子系统之间无交互,解决「外部调用复杂」问题,是单向简化访问。
-
中介者模式 :双向通信、多对多转一对多 。多个子系统相互交互,全部通过中介转发,解决「内部多对象耦合混乱」问题,是双向交互解耦。
核心一句话区分 :门面是对外简化调用 ,中介是对内解耦通信。
8.4 完整实战可运行代码(面试手撕版)
业务场景:多人聊天室场景,用户作为同事对象,聊天室作为中介者,用户发送消息全部通过聊天室转发,用户之间不直接通信。
① 抽象中介者接口
java
/**
* 抽象中介者:聊天室统一规范
*/
public interface ChatMediator {
// 消息转发方法
void sendMessage(String msg, UserColleague sender);
// 注册用户同事
void registerUser(UserColleague user);
}
② 抽象同事类(用户)
java
/**
* 抽象同事类:聊天室用户
*/
public abstract class UserColleague {
// 持有中介者引用
protected ChatMediator mediator;
protected String userName;
public UserColleague(ChatMediator mediator, String userName) {
this.mediator = mediator;
this.userName = userName;
}
// 发送消息
public abstract void sendMsg(String msg);
// 接收消息
public abstract void receiveMsg(String msg, String senderName);
}
③ 具体同事类(普通用户)
java
/**
* 具体同事类:聊天室普通用户
*/
public class ChatUser extends UserColleague{
public ChatUser(ChatMediator mediator, String userName) {
super(mediator, userName);
}
// 用户发送消息,交由中介者转发
@Override
public void sendMsg(String msg) {
System.out.println("【" + userName + "】发送消息:" + msg);
mediator.sendMessage(msg, this);
}
// 用户接收消息
@Override
public void receiveMsg(String msg, String senderName) {
System.out.println("【" + userName + "】接收" + senderName + "消息:" + msg);
}
public String getUserName() {
return userName;
}
}
④ 具体中介者(聊天室中枢)
java
import java.util.ArrayList;
import java.util.List;
/**
* 具体中介者:聊天室核心调度中枢
*/
public class ChatRoomMediator implements ChatMediator {
// 维护所有注册的用户同事
private List<ChatUser> userList = new ArrayList<>();
// 注册用户
@Override
public void registerUser(UserColleague user) {
if (user instanceof ChatUser) {
userList.add((ChatUser) user);
}
}
// 消息全局转发:除发送者外,推送给所有在线用户
@Override
public void sendMessage(String msg, UserColleague sender) {
if (!(sender instanceof ChatUser)) {
return;
}
ChatUser sendUser = (ChatUser) sender;
// 遍历所有用户,转发消息
for (ChatUser user : userList) {
// 不推送给自己
if (!user.getUserName().equals(sendUser.getUserName())) {
user.receiveMsg(msg, sendUser.getUserName());
}
}
}
}
⑤ 客户端测试调用
java
public class MediatorClient {
public static void main(String[] args) {
// 创建中介者聊天室
ChatMediator chatRoom = new ChatRoomMediator();
// 创建多个同事用户并注册
UserColleague zhangsan = new ChatUser(chatRoom, "张三");
UserColleague lisi = new ChatUser(chatRoom, "李四");
UserColleague wangwu = new ChatUser(chatRoom, "王五");
chatRoom.registerUser(zhangsan);
chatRoom.registerUser(lisi);
chatRoom.registerUser(wangwu);
// 张三发送消息,全局推送
zhangsan.sendMsg("大家好,欢迎进入聊天室!");
System.out.println("========================");
// 李四发送消息
lisi.sendMsg("大家好!");
}
}
}
8.5 核心优缺点深度解析
✅ 核心优点
-
彻底解耦多对象依赖:同事类之间无直接关联,所有交互通过中介中转,彻底消灭网状耦合
-
符合开闭原则:新增同事对象只需新增具体同事类并注册到中介,无需修改原有交互逻辑与其他对象代码
-
职责高度单一:同事类专注自身业务,中介类专注交互调度,职责拆分清晰,代码内聚性极强
-
交互逻辑统一收敛:所有通信、转发、调度规则集中在中介者中,统一维护、排查问题、迭代修改更便捷
-
简化对象关系:将N*N复杂依赖简化为N对1的简单依赖,极大降低架构复杂度
❌ 核心缺点
-
中介者极易臃肿:业务场景复杂、交互规则繁多时,中介者会承载所有调度逻辑,代码体量巨大、维护难度飙升
-
中枢单点风险:中介者是核心调度唯一入口,一旦出现异常,所有对象交互全部瘫痪
-
少量场景冗余:仅2个对象交互的简单场景,使用中介者会增加代码层级,过度设计
8.6 工程落地高频场景
-
即时通信场景:多人聊天室、群消息推送、在线协同编辑(多人操作同步)
-
业务协作场景:多级审批流、多模块业务联动、工单系统多角色协作
-
架构解耦场景:微服务网关调度、消息中间件转发、系统模块通信中枢
-
设备调度场景:多设备联动控制、智能家居设备交互、多终端状态同步
8.7 面试高频问答(必背)
问题1:中介者模式遵循哪些设计原则?
核心遵循三大原则:单一职责原则 (业务对象、交互调度职责拆分)、开闭原则 (新增交互对象无需修改核心代码)、迪米特法则(同事类最小化依赖,仅依赖中介者,不依赖其他业务对象)。
问题2:什么场景绝对不推荐使用中介者模式?
简单双向交互场景、业务逻辑简单且固定的场景不推荐使用,会造成过度设计、代码冗余、层级复杂,反而降低代码可读性与维护效率。中介者模式只适用于多对象网状耦合、交互复杂、频繁迭代扩展的场景。
8.8 面试满分终极总结
中介者模式是解决多对象多对多耦合的核心行为型模式,核心通过引入统一中介调度中枢,将复杂的网状双向依赖转化为星型单向依赖,彻底解耦同事对象之间的通信关联。所有交互逻辑统一收敛、集中管控,完美适配多人协作、多模块联动、复杂消息调度场景,有效降低系统耦合度、提升代码扩展性与可维护性,是分布式调度、即时通信、业务联动架构的经典落地方案。
9. 备忘录模式(Memento)------ 无侵入状态快照回滚
9.1 核心定义与底层思想
官方定义 :在不破坏封装性的前提下,捕获一个对象的内部状态,并将该状态保存为独立的备忘录快照,后续可随时将对象恢复到任意历史快照状态,实现状态备份与回滚。
通俗解读:给业务对象的关键状态"拍照存档",业务操作出错、参数修改异常时,无需手动重置数据,可一键回滚到历史正常状态,全程不暴露对象内部私有字段与实现细节。
核心底层思想 :状态快照隔离、无侵入备份、精准回溯、封装保护
核心解决痛点:
-
对象状态动态变更后无存档,出错无法回滚,只能手动重置重建,成本极高
-
直接暴露对象私有字段做备份,破坏封装性,存在数据篡改、安全风险
-
多步骤业务操作,需支持撤销、回滚、重试场景,无统一状态管理方案
-
多版本状态留存混乱,无法精准切换任意历史状态
9.2 核心三大角色(标准架构)
-
原发器(Originator) :核心业务对象,拥有需要备份的内部状态,负责创建备忘录快照 和根据快照恢复状态,是状态的生产者与恢复执行者。
-
备忘录(Memento) :状态快照载体,仅存储原发器的历史状态数据,只读不修改,私有化构造,仅允许原发器访问内部数据,保障状态安全。
-
看护人/管理者(Caretaker) :备忘录的统一管理者,负责存储、获取、删除备忘录快照,不读取、不修改快照内部数据,仅做容器管理。
核心设计精髓:状态数据闭环隔离,管理者只存不读,备忘录只存不改,原发器全权负责状态生成与恢复,完美保护封装性。
9.3 完整实战可运行代码(面试手撕版)
业务场景:文档编辑场景,支持多次编辑内容备份、一键回滚历史版本,模拟Word/记事本撤销恢复功能。
① 备忘录角色(文档快照)
java
/**
* 备忘录角色:文档状态快照
* 仅存储状态、不提供修改方法、私有化构造,保证数据安全
*/
public class DocumentMemento {
// 备份的文档内容状态
private final String content;
// 私有化构造:仅允许原发器创建快照
private DocumentMemento(String content) {
this.content = content;
}
// 仅对外提供获取状态方法,禁止修改
public String getContent() {
return content;
}
// 开放创建入口,仅限原发器调用
public static DocumentMemento create(String content){
return new DocumentMemento(content);
}
}
② 原发器角色(文档编辑器)
java
/**
* 原发器角色:文档编辑器
* 负责编辑内容、生成备份快照、恢复历史状态
*/
public class DocumentEditor {
// 当前文档内容状态
private String content;
// 编辑文档内容
public void edit(String content){
this.content = content;
}
// 创建备忘录快照,备份当前状态
public DocumentMemento saveMemento(){
return DocumentMemento.create(this.content);
}
// 根据备忘录快照,恢复历史状态
public void restoreMemento(DocumentMemento memento){
this.content = memento.getContent();
}
// 获取当前文档内容
public String getContent() {
return content;
}
}
③ 看护人角色(版本管理者)
java
import java.util.ArrayList;
import java.util.List;
/**
* 看护人角色:文档版本管理者
* 只负责存储、管理快照,不读取、不修改快照内容
*/
public class DocumentCaretaker {
// 存储多版本备忘录快照
private List<DocumentMemento> mementoList = new ArrayList<>();
// 保存快照
public void addMemento(DocumentMemento memento){
mementoList.add(memento);
}
// 获取指定版本快照
public DocumentMemento getMemento(int index){
return mementoList.get(index);
}
// 获取快照版本数量
public int getVersionCount(){
return mementoList.size();
}
}
④ 客户端测试调用(多版本备份+回滚)
java
public class MementoClient {
public static void main(String[] args) {
// 1. 创建原发器和管理者
DocumentEditor editor = new DocumentEditor();
DocumentCaretaker caretaker = new DocumentCaretaker();
// 2. 第一次编辑并备份版本1
editor.edit("第一版:设计模式基础内容");
caretaker.addMemento(editor.saveMemento());
System.out.println("当前文档内容:" + editor.getContent());
// 3. 第二次编辑并备份版本2
editor.edit("第二版:补充六大设计原则");
caretaker.addMemento(editor.saveMemento());
System.out.println("当前文档内容:" + editor.getContent());
// 4. 第三次编辑(未备份,模拟错误修改)
editor.edit("错误内容:乱改文档数据");
System.out.println("错误修改后内容:" + editor.getContent());
// 5. 回滚到第二版历史状态
editor.restoreMemento(caretaker.getMemento(1));
System.out.println("回滚后文档内容:" + editor.getContent());
// 6. 回滚到第一版历史状态
editor.restoreMemento(caretaker.getMemento(0));
System.out.println("再次回滚后内容:" + editor.getContent());
}
}
9.4 核心优缺点深度解析
✅ 核心优点
-
封装性完美无损:状态备份无需暴露对象私有字段,通过专属备忘录隔离状态,不破坏原有类封装特性
-
精准状态回溯:支持多版本快照留存,可自由回滚任意历史状态,完美适配撤销、重试、回滚业务
-
职责高度单一:原发器管状态生成与恢复、备忘录管状态存储、看护人管版本管理,三层职责完全拆分
-
无侵入扩展:无需修改原发器业务代码,即可实现状态备份回滚能力,符合开闭原则
❌ 核心缺点
-
内存资源消耗:对象状态复杂、版本过多时,大量备忘录快照会占用高额内存,造成资源冗余
-
维护成本提升:每一个需要备份的对象,都需要配套备忘录类,轻微造成类数量膨胀
-
不支持增量备份:原生模式为全量状态备份,频繁备份相同数据会产生大量重复冗余快照
9.5 工程落地高频场景
-
办公编辑场景:文档/表格/图片编辑撤销重做、历史版本回滚(Word/PS/编辑器核心原理)
-
业务操作场景:订单编辑回滚、审批操作撤销、表单修改回溯、业务数据容错恢复
-
游戏场景:游戏存档、关卡状态备份、角色属性回滚
-
技术架构场景:配置项版本备份、缓存状态快照、事务操作回滚、定时任务状态恢复
9.6 面试高频问答(必背)
问题1:备忘录模式如何保证封装性不被破坏?
核心通过角色权限隔离实现:备忘录私有化构造方法,仅允许原发器创建快照;仅提供状态获取方法,禁止外部修改;看护人只负责存储快照,无法读取和修改快照内部状态,全程隔离状态数据,不暴露原发器私有字段,完美保护封装性。
问题2:备忘录模式和命令模式撤销的区别?
命令模式 :基于操作行为撤销,记录操作指令,反向执行撤销逻辑,适合简单操作回滚;
备忘录模式 :基于状态快照回滚,直接保存对象完整状态,无需关注操作过程,适合复杂对象、多步骤操作的精准状态恢复。
问题3:如何优化备忘录内存占用过高问题?
工程优化方案:实现增量备忘录,只备份变更的状态字段,而非全量备份;设置快照过期清理机制,自动删除老旧无用版本;采用序列化压缩存储快照,降低内存占用。
9.7 面试满分终极总结
备忘录模式是实现对象状态备份与回滚的专属行为型模式,通过原发器、备忘录、看护人三层角色隔离,在不破坏封装性的前提下实现对象状态快照留存与精准回溯。核心解决业务操作撤销、数据容错恢复、历史版本回滚问题,无需侵入业务代码,兼顾代码扩展性与数据安全性,广泛应用于文档编辑、业务回溯、游戏存档、配置版本管理等场景。
10. 访问者模式(Visitor)------ 数据结构与操作解耦
10.1 核心定义与底层思想
官方定义 :访问者模式是一种将数据结构与数据操作分离的行为型设计模式,在不修改原有数据元素类结构与代码的前提下,可以动态新增作用于元素的各类操作行为,实现操作的无限扩展,完美契合开闭原则。
通俗解读:固定不变的对象结构,频繁新增业务操作时,无需改动原对象代码,所有新增操作统一交由访问者实现,把"操作行为"从实体类中抽离出来,单独扩展、统一管理。
核心底层思想 :结构固化、操作抽离、行为外置、动态扩展
核心解决痛点:
-
固定数据结构频繁新增业务操作,每次新增功能都需修改实体类代码,严重违背开闭原则
-
实体类承载过多业务逻辑,职责混杂、代码臃肿,违背单一职责原则
-
多维度、多类型统计计算逻辑分散,无法统一管控、统一扩展
-
不同操作逻辑耦合在实体中,复用性差、维护成本极高
10.2 核心组成组件(标准五组件)
-
抽象访问者(Visitor):顶层访问规范,为每一种具体元素类定义专属访问方法,统一所有操作行为接口
-
具体访问者(ConcreteVisitor):实现抽象访问者接口,承载具体业务操作(统计、计算、导出、校验等),每类访问者对应一类业务维度操作
-
抽象元素(Element) :数据实体顶层接口,定义统一的接收访问方法
accept(Visitor),接收访问者调度 -
具体元素(ConcreteElement):固定不变的数据实体类,实现接收方法,主动接收访问者并交由对应方法处理自身数据
-
对象结构(ObjectStructure):元素集合容器,负责存储所有具体元素,提供遍历元素、批量接收访问者的能力,统一调度访问流程
10.3 核心双分派机制(面试高频核心)
访问者模式依托双分派实现精准匹配:
第一次分派(静态分派):元素调用accept方法,传入访问者,确定访问入口;
第二次分派(动态分派):元素在accept内部回调访问者的专属方法,根据自身元素类型,精准匹配对应操作逻辑。
核心价值:无需if-else判断元素类型,自动匹配对应操作,彻底消灭类型判断冗余代码。
10.4 完整实战可运行代码(面试手撕版)
业务场景:员工数据结构化统计,固定员工结构(普通员工、管理者),实现薪资统计、绩效评分两类不同维度的访问操作,后续可无限新增统计维度,无需修改员工实体类。
① 抽象访问者接口
java
/**
* 抽象访问者:为不同员工元素定义专属访问方法
*/
public interface Visitor {
// 访问普通员工
void visitCommonEmployee(CommonEmployee employee);
// 访问管理者
void visitManagerEmployee(ManagerEmployee employee);
}
② 抽象元素接口(员工)
java
/**
* 抽象元素:员工顶层接口
*/
public interface Employee {
// 接收访问者访问
void accept(Visitor visitor);
}
③ 具体元素实现(普通员工、管理者)
java
// 具体元素:普通员工
public class CommonEmployee implements Employee {
private String name;
private double salary;
private String job;
public CommonEmployee(String name, double salary, String job) {
this.name = name;
this.salary = salary;
this.job = job;
}
// 接收访问者,回调对应普通员工访问方法
@Override
public void accept(Visitor visitor) {
visitor.visitCommonEmployee(this);
}
// getter方法
public String getName() { return name; }
public double getSalary() { return salary; }
public String getJob() { return job; }
}
// 具体元素:管理者
public class ManagerEmployee implements Employee {
private String name;
private double salary;
private String department;
public ManagerEmployee(String name, double salary, String department) {
this.name = name;
this.salary = salary;
this.department = department;
}
// 接收访问者,回调对应管理者访问方法
@Override
public void accept(Visitor visitor) {
visitor.visitManagerEmployee(this);
}
// getter方法
public String getName() { return name; }
public double getSalary() { return salary; }
public String getDepartment() { return department; }
}
④ 具体访问者实现(薪资统计、绩效评分)
java
// 具体访问者1:薪资统计访问者
public class SalaryCountVisitor implements Visitor {
// 总薪资
private double totalSalary = 0;
@Override
public void visitCommonEmployee(CommonEmployee employee) {
totalSalary += employee.getSalary();
System.out.println("统计普通员工【" + employee.getName() + "】薪资:" + employee.getSalary());
}
@Override
public void visitManagerEmployee(ManagerEmployee employee) {
totalSalary += employee.getSalary();
System.out.println("统计管理者【" + employee.getName() + "】薪资:" + employee.getSalary());
}
// 获取总薪资
public double getTotalSalary() {
return totalSalary;
}
}
// 具体访问者2:绩效评分访问者
public class ScoreJudgeVisitor implements Visitor {
@Override
public void visitCommonEmployee(CommonEmployee employee) {
// 普通员工根据岗位打分
int score = employee.getJob().contains("开发") ? 90 : 80;
System.out.println("普通员工【" + employee.getName() + "】绩效评分:" + score);
}
@Override
public void visitManagerEmployee(ManagerEmployee employee) {
// 管理者根据部门打分
int score = employee.getDepartment().equals("核心业务部") ? 95 : 85;
System.out.println("管理者【" + employee.getName() + "】绩效评分:" + score);
}
}
⑤ 对象结构容器(员工集合管理)
java
import java.util.ArrayList;
import java.util.List;
/**
* 对象结构:员工集合容器
* 统一存储元素、批量调度访问者
*/
public class EmployeeStructure {
private List<Employee> employeeList = new ArrayList<>();
// 添加员工元素
public void addEmployee(Employee employee) {
employeeList.add(employee);
}
// 批量接受访问者访问
public void acceptVisitor(Visitor visitor) {
for (Employee employee : employeeList) {
employee.accept(visitor);
}
}
}
⑥ 客户端测试调用
java
public class VisitorClient {
public static void main(String[] args) {
// 1. 构建对象结构,添加员工元素
EmployeeStructure structure = new EmployeeStructure();
structure.addEmployee(new CommonEmployee("张三", 8000, "Java开发"));
structure.addEmployee(new CommonEmployee("李四", 6000, "测试"));
structure.addEmployee(new ManagerEmployee("王五", 15000, "核心业务部"));
// 2. 薪资统计访问
SalaryCountVisitor salaryVisitor = new SalaryCountVisitor();
structure.acceptVisitor(salaryVisitor);
System.out.println("员工总薪资:" + salaryVisitor.getTotalSalary());
System.out.println("============================");
// 3. 绩效评分访问
ScoreJudgeVisitor scoreVisitor = new ScoreJudgeVisitor();
structure.acceptVisitor(scoreVisitor);
}
}
10.5 核心优缺点深度解析
✅ 核心优点
-
完美契合开闭原则:新增业务操作、新增统计维度,只需新增访问者类,无需修改任何实体元素代码
-
彻底解耦结构与操作:固定数据结构,操作行为完全外置,实体类只保留基础属性,职责单一纯粹
-
操作逻辑集中管控:同一维度的所有操作收敛在一个访问者类中,代码集中、便于维护、统一迭代
-
消灭冗余分支代码:依托双分派机制,自动匹配元素类型与操作,无需大量if-else判断类型
-
行为可复用可扩展:访问者操作可复用在任意同类元素结构上,支持动态新增、替换操作逻辑
❌ 核心缺点
-
元素结构扩展困难:新增元素子类时,需要修改抽象访问者及所有具体访问者代码,违背开闭原则
-
依赖元素内部细节:访问者需要获取元素内部属性,一定程度暴露实体类内部数据,破坏封装性
-
类数量膨胀:操作维度过多时,会产生大量访问者实现类,增加代码体量
-
适用场景受限 :仅适合数据结构固定、操作行为频繁扩展的场景,结构频繁变动场景完全不适用
10.6 工程落地高频场景
-
树形结构处理:部门-员工树形结构、菜单树形结构的多维度统计、遍历、导出
-
数据报表场景:同一批数据多维度汇总计算、多格式导出(Excel/PDF)、数据校验
-
规则引擎场景:固定实体模型,动态新增业务规则、校验规则、风控规则
-
源码框架场景:Java AST抽象语法树遍历、MyBatis SQL节点解析、编译器语法分析
-
权限系统场景:角色、用户、资源固定结构,动态新增权限校验、数据统计、日志记录操作
10.7 面试高频问答(必背)
问题1:访问者模式的适用前提是什么?
核心前提:数据结构稳定不变,操作行为频繁扩展。如果元素结构经常新增、修改、删除,绝对不推荐使用,会导致访问者代码大面积修改,维护成本剧增。
问题2:访问者模式最大的优缺点是什么?
最大优点:操作扩展完全遵循开闭原则,新增功能不改实体代码;
最大缺点:元素结构扩展极度困难,牵一发而动全身。
问题3:访问者模式体现了哪些设计原则?
核心体现:单一职责 (实体存数据、访问者做操作)、开闭原则(操作扩展) 、迪米特法则(统一调度访问,解耦交互)。
10.8 面试满分终极总结
访问者模式是专门解决固定数据结构、动态扩展操作的行为型模式,核心通过双分派机制,将数据实体与业务操作彻底解耦,把所有操作行为外置到独立访问者中。新增业务维度无需修改实体代码,完美适配报表统计、树形遍历、规则扩展等场景。该模式优缺点极其鲜明,仅适用于结构稳定、操作多变的业务场景,是复杂数据多维度处理的最优设计方案。
11. 解释器模式(Interpreter)------ 自定义语法规则解析
11.1 核心定义与底层思想
官方定义:给定一门自定义语言的文法规则,构建对应的抽象语法树,定义表达式解释器,用于解析、执行自定义语法语句,实现自定义脚本、规则表达式的解析与运算。
通俗解读:自己定义一套语法规则(如加减运算、条件判断、规则表达式),自己写解析器,让程序能够识别并执行自定义语法,实现灵活的规则运算。
核心底层思想 :文法抽象、表达式拆分、语法树解析、递归执行
核心解决痛点:
-
固定代码无法适配动态可变的业务规则,每次修改规则需要改代码重启服务
-
自定义表达式、脚本语句无法被程序识别解析,无法实现动态规则运算
-
复杂组合规则、条件运算、公式计算无统一解析方案,代码杂乱冗余
11.2 核心组成组件(标准六组件)
-
抽象表达式(AbstractExpression) :顶层统一接口,定义核心解释方法
interpret(),规范所有表达式解析行为 -
终结符表达式(TerminalExpression):最小语法单元,对应变量、常量、固定字符,是语法树的叶子节点
-
非终结符表达式(NonTerminalExpression):组合表达式,对应运算、条件、逻辑组合,包含子表达式,实现递归解析
-
上下文环境(Context):存储全局解析上下文、变量参数、临时数据,为表达式解析提供数据支撑
-
语法解析器:拆分语句、校验文法、构建抽象语法树,组装各类表达式节点
-
客户端:传入自定义语法语句,调用解析器完成解析与执行
11.3 完整实战可运行代码(面试最简手撕版)
业务场景:实现简单自定义加减运算解释器,解析并计算自定义表达式(如 a+b-c),实现动态公式运算。
① 上下文环境(存储变量参数)
java
import java.util.HashMap;
import java.util.Map;
/**
* 上下文环境:存储表达式变量参数
*/
public class Context {
// 存储变量名-变量值
private Map<String, Integer> paramMap = new HashMap<>();
// 设置变量
public void putParam(String key, Integer value) {
paramMap.put(key, value);
}
// 获取变量值
public Integer getParam(String key) {
return paramMap.get(key);
}
}
② 抽象表达式接口
java
/**
* 抽象表达式:统一解释执行规范
*/
public interface Expression {
// 解释执行方法,返回运算结果
int interpret(Context context);
}
③ 终结符表达式(变量表达式)
java
/**
* 终结符表达式:变量解析(叶子节点)
*/
public class VarExpression implements Expression {
private String key;
public VarExpression(String key) {
this.key = key;
}
// 从上下文获取变量值
@Override
public int interpret(Context context) {
return context.getParam(key);
}
}
④ 非终结符表达式(加减运算)
java
// 加法非终结符表达式
public class AddExpression implements Expression {
// 左右子表达式
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
// 递归解析左右表达式并求和
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
// 减法非终结符表达式
public class SubExpression implements Expression {
private Expression left;
private Expression right;
public SubExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
// 递归解析左右表达式并求差
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
}
⑤ 客户端测试调用
java
public class InterpreterClient {
public static void main(String[] args) {
// 1. 构建上下文,赋值变量
Context context = new Context();
context.putParam("a", 10);
context.putParam("b", 5);
context.putParam("c", 3);
// 2. 组装表达式:a + b - c
Expression expression = new SubExpression(
new AddExpression(new VarExpression("a"), new VarExpression("b")),
new VarExpression("c")
);
// 3. 解析执行
int result = expression.interpret(context);
System.out.println("表达式 a+b-c 运算结果:" + result);
}
}
11.4 核心优缺点深度解析
✅ 核心优点
-
灵活扩展语法:新增运算规则、语法逻辑只需新增表达式类,符合开闭原则
-
动态规则解析:支持自定义表达式、脚本语句解析,无需改代码即可调整业务规则
-
分层清晰、递归解耦:拆分终结符与非终结符,递归解析复杂语法,逻辑分层清晰
-
规则可复用:各类表达式节点可自由组合,适配不同复杂规则场景
❌ 核心缺点
-
类数量爆炸:语法规则越多,表达式子类越多,代码体量急剧膨胀
-
递归解析性能差:复杂语法树递归层级深,存在栈溢出风险,解析效率低
-
维护难度极高:自定义文法复杂后,语法树调试、问题排查极其繁琐
-
适用场景极窄:日常业务开发极少使用,仅专属语法解析场景可用
11.5 工程落地高频场景
-
规则引擎场景:业务规则表达式、风控规则、薪资计算公式解析
-
语法解析场景:SQL语句解析、正则表达式解析、编译器语法分析
-
脚本引擎场景:自定义配置脚本、动态表达式运算、低代码平台规则解析
-
框架底层场景:Spring EL表达式、MyBatis OGNL表达式解析核心原理
11.6 面试高频问答(必背)
问题1:解释器模式日常开发为什么很少用?
因为其缺点极其明显:类膨胀严重、递归性能差、维护成本高,日常业务开发无需自定义语法解析,现有框架(Spring EL、规则引擎)已封装好解析能力,业务层无需手动实现解释器模式,仅框架底层会使用。
问题2:解释器模式核心应用价值?
核心价值是实现动态可配置规则,将硬编码的业务规则转化为可配置的自定义表达式,实现规则无需改代码、动态生效,适配低代码、规则引擎、动态公式场景。
11.7 面试满分终极总结
解释器模式是用于自定义语法解析与执行的小众行为型模式,核心通过拆分终结符与非终结符表达式,构建抽象语法树递归解析自定义语句,实现动态规则、自定义表达式的运算执行。可灵活扩展语法规则、实现业务动态配置,但存在类膨胀、性能低、维护难的问题,日常业务极少使用,主要应用于规则引擎、表达式解析、编译语法分析等框架底层场景。
五、高频模式对比图解速记(面试区分易混模式|完整版)
本节汇总面试90%高频易混淆设计模式,摒弃模糊概念,从核心目的、行为特征、适用场景、代码差异四个维度精准区分,搭配绝杀口诀,彻底解决模式混淆问题,适配口述面试+笔试辨析题型。
1. 装饰器模式 vs 代理模式(最易混结构型模式)
核心共性 :均实现对象包装、功能增强、不修改原类代码,基于组合实现扩展,符合开闭原则。
核心本质差异
-
装饰器 :叠加增强功能 ,无访问控制,专注对原对象的能力层层叠加,可无限嵌套包装,同类型扩展。
-
代理模式 :管控对象访问 ,侧重拦截、控制、转发、权限校验,不专注功能叠加,是对目标对象的访问代理。
场景精准区分
-
装饰器:Java IO流包装、Spring缓存功能叠加、动态追加业务能力(如日志+权限+限流多层叠加)。
-
代理模式:Spring AOP代理、MyBatis Mapper代理、远程调用代理、权限拦截、延迟加载。
绝杀口诀 :装饰加功能,代理管访问
2. 策略模式 vs 状态模式(行为型Top混淆)
核心共性 :均通过多态实现分支逻辑剥离、消除if-else,统一顶层接口、多子类实现。
核心本质差异
-
策略模式 :平等算法互换,所有策略无固定顺序、无依赖关系,人为主动切换算法,对象无状态流转。
-
状态模式 :有序状态自动流转,状态存在固定前置/后置关系,由当前状态驱动自动切换,无需人工干预,强依赖场景状态。
场景精准区分
-
策略模式:支付渠道切换、优惠算法切换、排序算法替换、限流策略切换。
-
状态模式:订单状态(待支付→已支付→发货→完成)、工单流转、审批状态、任务生命周期。
绝杀口诀 :策略主动换算法,状态自动走流程
3. 适配器模式 vs 桥接模式(结构型解耦混淆)
核心共性 :均解决接口不兼容、代码耦合、扩展受限问题,实现系统适配与解耦。
核心本质差异
-
适配器 :事后兼容、亡羊补牢 。针对已存在的不兼容接口、老旧系统、第三方接口,做适配转换,解决现有代码不兼容问题,属于被动适配。
-
桥接模式 :事前解耦、双向扩展 。提前拆分抽象与实现两层,彻底解除两层绑定关系,支持双向独立扩展,从根源避免类爆炸,属于主动架构优化。
场景精准区分
-
适配器:新旧系统对接、第三方SDK接口适配、老代码改造兼容、多数据源接口统一。
-
桥接模式:支付渠道+支付类型、消息类型+发送渠道、操作系统+软件功能等双层维度扩展场景。
绝杀口诀 :适配器补兼容漏洞,桥接器拆双层架构
4. 门面模式(外观) vs 中介者模式(最易混解耦模式)
核心共性 :均通过中间层封装交互,降低系统耦合,简化调用逻辑。
核心本质差异
-
门面模式 :单向调用、一对多、对外简化。客户端调用门面,门面调用底层子系统,子系统之间无任何交互,仅简化外部访问入口。
-
中介者模式 :双向通信、多对多、对内解耦。多个子系统/同事类存在复杂双向交互,通过中介转发消息,消灭网状耦合。
场景精准区分
-
门面模式:复杂业务接口封装(下单接口整合库存/支付/物流)、多子系统统一对外入口。
-
中介者模式:多人聊天室、多模块联动审批、微服务消息中转、多设备协同调度。
绝杀口诀 :门面对外简化调用,中介对内解耦通信
5. 工厂模式 vs 建造者模式(创建型核心混淆)
核心共性 :均封装对象创建过程,解耦创建与使用,避免硬编码new对象。
核心本质差异
-
工厂模式(工厂方法/抽象工厂) :侧重产品整体实例创建,统一产出完整产品,不关注构建细节与步骤,主打「快速产出标准化产品」。
-
建造者模式 :侧重复杂对象分步组装,拆分构建步骤、支持参数选配、灵活组合不同属性,主打「精细化定制复杂对象」。
场景精准区分
-
工厂模式:标准化产品创建(支付产品、日志类型、数据源)、单一维度实例扩展。
-
建造者模式:多参数复杂对象(Builder实体类、SQL语句、复杂配置、链式组装对象)。
绝杀口诀 :工厂造整品,建造者拼细节
6. 原型模式 vs 新建对象(创建型易混辨析)
核心差异
-
new新建:从零初始化对象,执行构造方法,性能低,适合简单对象、低频创建场景。
-
原型克隆 :基于已有对象快照复制,跳过初始化流程,性能极高,适合复杂大对象、高频创建、参数一致对象。
绝杀口诀 :new从零造,克隆从旧复制
7. 单例模式 vs 原型Bean(Spring专属面试对比)
-
单例:JVM/容器全局唯一,全局共享同一实例,适合无状态资源,高性能、省内存。
-
原型:每次获取均新建实例,多线程无共享竞争,适合有状态、可变数据对象。
绝杀口诀 :单例全局唯一,原型次次新建
8. 命令模式 vs 备忘录模式(撤销回滚场景混淆)
核心共性 :均支持业务撤销、操作回滚,实现业务容错。
核心本质差异
-
命令模式 :基于操作行为撤销,记录执行的命令指令,通过反向执行指令实现回滚,依赖操作过程。
-
备忘录模式 :基于状态快照回滚,不关注操作过程,直接保存对象完整历史状态,精准回溯数据。
绝杀口诀 :命令退操作,备忘录退状态
9. 迭代器模式 vs 遍历循环(底层原理辨析)
-
普通for/foreach:语法层面遍历,耦合容器结构,不同容器遍历方式不统一。
-
迭代器模式:封装遍历逻辑,统一所有容器遍历规范,屏蔽底层存储差异,支持安全遍历删除。
绝杀口诀 :循环是语法,迭代是统一规范
10. 访问者模式 vs 普通工具类(操作扩展辨析)
-
普通工具类:操作逻辑静态固化,新增功能需改工具类,数据与操作隐性耦合。
-
访问者模式:数据结构固定,操作动态扩展,新增业务维度无需修改实体,完美契合开闭原则。
绝杀口诀 :工具类改代码扩展,访问者加类扩展
全网超全|易混模式终极对照表(面试速查)
|-----------|--------------------|------------|
| 易混模式组 | 核心区别一句话 | 核心关键词 |
| 装饰器 vs 代理 | 装饰叠加功能,代理管控访问权限 | 增强VS控制 |
| 策略 vs 状态 | 策略主动换算法,状态自动走流程 | 无状态VS有状态 |
| 适配器 vs 桥接 | 适配器兼容旧代码,桥接器解耦新架构 | 事后适配VS事前解耦 |
| 门面 vs 中介者 | 门面对外简化调用,中介对内解耦通信 | 单向VS双向 |
| 工厂 vs 建造者 | 工厂批量造成品,建造者分步拼细节 | 整体创建VS分步组装 |
| 命令 vs 备忘录 | 命令撤销操作行为,备忘录回滚数据状态 | 行为回溯VS状态回溯 |
六、框架底层对应(Java 面试加分项,源码落地|完整版)
本章节精准对应23种设计模式 + JDK底层 + Spring全家桶 + 主流开源框架落地源码场景,摒弃空泛举例,全部为面试高频源码考点,精准回答「你项目/框架中哪里用到过设计模式」经典面试题,是区分初级与中级Java开发的核心加分项。
核心面试话术:所有框架底层均依托设计模式实现解耦、扩展、复用,23种模式并非凭空理论,而是Java源码、Spring框架、中间件的核心底层设计思想。
6.1 创建型模式(5种)框架源码落地【超全深挖版|面试满分】
核心总览 :创建型模式核心解决对象创建耦合、实例管控混乱、资源浪费、构建复杂问题,是Spring IoC容器、JDK工具类、中间件资源初始化的底层核心思想,所有框架对象实例化逻辑,均基于5种创建型模式衍生落地,也是面试「框架底层设计」高频提问模块。
(1)单例模式(Singleton)
✅ JDK核心落地源码 :java.lang.Runtime(经典饿汉单例,全局唯一运行时实例)、java.awt.Toolkit、系统时区/配置工具类;
✅ Spring核心落地场景 :Spring IoC容器默认Singleton单例Bean(容器级单例)、Spring容器上下文对象、配置属性工具类、全局注册器;
✅ 中间件落地:RedisTemplate、数据库连接池、线程池、MQ客户端全局实例、注册中心客户端;
✅ 底层源码细节 :Spring通过三级缓存保障单例Bean创建唯一性,解决循环依赖问题,区别于JVM全局单例;饿汉式天然线程安全,适配框架全局无状态资源;
✅ 面试深挖考点:
-
Spring单例是容器级单例,仅当前IoC容器内唯一,非JVM全局唯一;
-
Spring单例Bean不保证线程安全,无状态Bean适配单例,有状态Bean必须使用原型模式;
-
框架底层核心资源(连接池、客户端)均采用单例,减少频繁创建销毁开销;
✅ 工程踩坑:单例Bean禁止定义可变成员变量,否则多线程并发会出现数据脏读、覆盖问题。
(2)工厂方法模式(Factory Method)
✅ 核心定义回顾 :定义抽象工厂接口,将具体产品实例化延迟到子类工厂,实现单一产品维度扩展,完美契合开闭原则;
✅ JDK核心落地源码 :java.util.Calendar#getInstance()(日历工厂,根据时区创建对应日历实例)、java.text.NumberFormat(数字格式化工厂)、java.nio.charset.Charset(字符集工厂)、日志框架org.slf4j.LoggerFactory;
✅ Spring核心落地场景 :顶级Bean工厂接口BeanFactory、ListableBeanFactory、HierarchicalBeanFactory子类工厂;
✅ 底层源码细节 :BeanFactory 定义getBean()统一创建规范,不同子类工厂实现不同Bean的实例化逻辑,新增Bean类型无需修改工厂顶层接口,仅新增子类实现;
✅ 面试深挖考点:
-
工厂方法模式核心是一厂一品,专注单一产品扩展;
-
Spring BeanFactory是典型工厂方法落地,解耦Bean创建与业务使用;
-
JDK各类工具工厂,屏蔽底层实例化细节,使用者无需关注对象创建逻辑。
(3)抽象工厂模式(Abstract Factory)
✅ 核心定义回顾 :工厂方法的升级版,专注产品族成套创建,生产一组关联/依赖的产品,保证组件配套兼容;
✅ JDK核心落地源码 :java.sql.Connection数据库连接工厂(不同数据库驱动生产连接、Statement成套产品)、javax.xml.parsers.DocumentBuilderFactory XML解析产品族工厂;
✅ Spring核心落地场景 :ApplicationContext 高级抽象工厂(BeanFactory的子接口)、WebApplicationContext web场景专属工厂;
✅ 底层源码细节 :ApplicationContext 不仅继承BeanFactory实现Bean创建,还配套提供资源加载、事件发布、环境配置、国际化等一整套框架能力产品族,保证Spring核心组件配套兼容、统一初始化;
✅ MyBatis落地场景 :SqlSessionFactory 生产SqlSession、Mapper、事务等成套数据库操作产品;
✅ 面试深挖考点:
-
抽象工厂解决产品族兼容问题,区别于工厂方法的单一产品扩展;
-
Spring ApplicationContext是典型抽象工厂,支撑框架全套核心能力;
-
多数据源、多驱动适配场景,优先使用抽象工厂保证组件配套性。
(4)建造者模式(Builder)
✅ 核心定义回顾:分步组装复杂对象,支持参数选配、链式调用,解决多参数复杂对象构造臃肿、参数混乱问题;
✅ JDK核心落地源码 :java.lang.StringBuilder、java.lang.StringBuffer(append链式分步拼接字符串)、java.nio.ByteBuffer 缓冲区构建;
✅ Spring核心落地场景 :SqlSessionBuilder、BeanDefinitionBuilder(分步构建Bean定义信息)、WebClient.Builder(SpringWebflux请求构建)、UriComponentsBuilder URL构建;
✅ 工程常用落地 :Lombok @Builder 注解、复杂DTO/VO链式构建、HTTP请求参数组装、配置类分步构建;
✅ 底层源码细节:通过静态内部类承载构建逻辑,拆分初始化、参数赋值、组装、返回步骤,支持按需选配参数,避免多参数构造方法参数顺序错乱、空值冗余问题;
✅ 面试深挖考点:
-
建造者侧重分步组装、灵活选配 ,工厂模式侧重整体快速产出;
-
所有复杂多参数对象、链式构建场景,均优先使用建造者模式;
-
框架中复杂配置、会话、请求对象,均通过建造者规避构建逻辑混乱问题。
(5)原型模式(Prototype)
✅ 核心定义回顾:通过对象克隆替代new初始化,基于已有对象快照复制,大幅提升复杂对象创建性能;
✅ JDK核心落地源码 :java.lang.Object#clone()(原生克隆接口)、java.util.ArrayList/HashMap 集合克隆、对象序列化深克隆;
✅ Spring核心落地场景 :Bean作用域 scope="prototype"(原型Bean);
✅ 底层源码细节 :Spring容器初始化时仅创建一个原型Bean模板,每次调用getBean() 时,基于模板对象克隆生成全新实例,而非重新执行构造方法初始化;
✅ 中间件落地:MyBatis Mapper实例克隆、缓存对象快照复制、高频复用复杂业务对象克隆;
✅ 面试深挖考点:
-
原型模式核心优势:跳过构造初始化,高性能创建复杂对象;
-
Spring原型Bean与单例Bean核心区别:原型Bean每次获取均为新实例,天然无线程安全问题,适合有状态对象;
-
必须区分浅克隆/深克隆:浅克隆仅复制引用,引用对象共享;深克隆完全复制对象,数据独立互不干扰;
✅ 工程适用场景:复杂大对象、高频创建、参数重复度高的业务对象,规避new初始化的性能损耗。
6.2 结构型模式(7种)框架源码落地【超全深挖版|面试满分】
核心总览 :结构型模式核心解决对象结构僵硬、接口不兼容、系统层级复杂、资源冗余、架构适配困难问题,核心思想为「多用组合、少用继承」,通过包装、适配、组合、分层、复用等方式改造已有对象结构,不修改源码即可优化架构。是Spring、JDK、MyBatis、Tomcat等框架分层设计、功能增强、系统兼容、性能优化的核心底层方案,面试高频提问「框架哪里用到结构型模式、解决了什么问题」。
(1)代理模式(静态/动态代理)------ 访问管控、功能增强核心
✅ 核心定义回顾:通过代理对象包装目标对象,拦截访问请求,在不修改原对象源码的前提下,实现访问控制、功能增强、逻辑解耦,是AOP思想的核心底层。
✅ JDK核心落地源码 :java.lang.reflect.Proxy JDK动态代理核心类、java.rmi 远程方法调用代理、静态代理事务封装。
✅ Spring核心落地场景:
-
Spring AOP核心实现:
JdkDynamicAopProxy(接口代理)、CglibAopProxy(子类代理),实现切面拦截、日志、事务、限流增强; -
Spring事务代理:通过代理拦截业务方法,自动开启/提交/回滚事务;
-
延迟加载代理:Spring懒加载Bean、动态依赖代理。
✅ MyBatis落地场景 :MapperProxy Mapper接口动态代理,无需手写DAO实现类,拦截接口方法、拼接SQL、执行数据库操作。
✅ 其他框架落地:Feign远程调用代理、Dubbo服务代理、RedisTemplate操作代理、权限拦截代理。
✅ 底层源码细节:
-
JDK动态代理:基于接口生成代理类,实现InvocationHandler回调拦截,仅支持接口代理;
-
CGLIB代理:基于ASM字节码生成子类,重写目标方法,支持类代理,无接口限制;
-
Spring代理策略:目标类有接口优先JDK代理,无接口默认CGLIB代理(SpringBoot 2.0+默认CGLIB)。
✅ 面试深挖考点:
-
代理与装饰器核心区别:代理侧重访问管控、拦截增强 ,装饰器侧重纯功能叠加;
-
动态代理解决静态代理类爆炸问题,是框架无侵入增强的核心;
-
AOP底层本质就是动态代理+责任链模式,实现方法横向扩展。
✅ 工程落地价值:统一拦截业务方法,实现非侵入式日志、事务、权限、监控,彻底解耦业务逻辑与通用增强逻辑。
(2)装饰器模式(装饰模式)------ 多层功能叠加、动态增强
✅ 核心定义回顾 :基于同类型对象嵌套包装,层层叠加功能,支持无限动态扩展能力,不修改原对象源码,完全契合开闭原则,核心是同类型功能增强。
✅ JDK核心落地源码:IO流体系经典落地(最核心面试案例):
-
基础节点流:
FileReader/FileWriter/FileInputStream/FileOutputStream(原生基础流); -
装饰缓冲流:
BufferedReader/BufferedInputStream嵌套包装基础流,叠加缓冲读写能力; -
转换流:
InputStreamReader/OutputStreamWriter包装字节流,叠加编码转换能力;
✅ Spring核心落地场景:
-
BeanWrapper:包装Bean对象,增强属性赋值、类型转换、字段映射能力; -
Spring Cache装饰:对原生缓存实现类层层包装,叠加过期策略、缓存淘汰、日志监控;
-
权限装饰器:对基础权限校验类叠加角色校验、数据权限校验。
✅ MyBatis落地场景 :ResultSetWrapper 包装原生结果集,叠加字段映射、类型适配、空值处理能力。
✅ 底层源码细节 :所有装饰器与被装饰者实现同一顶层接口,构造方法传入原对象,通过嵌套调用实现多层功能叠加,可自由组合、按需装配。
✅ 面试深挖考点:
-
装饰器无限嵌套、灵活组合,无类爆炸问题,优于继承扩展;
-
核心特征:同接口、嵌套包装、功能叠加、不改变原对象核心逻辑;
-
区别代理模式:无访问拦截、无权限管控,纯粹增强功能。
✅ 工程落地价值:替代继承实现动态功能扩展,多层能力按需叠加,代码灵活度极高。
(3)适配器模式(适配模式)------ 异构兼容、抹平差异
✅ 核心定义回顾 :将原有不兼容、异构的接口、类、系统,通过适配器转换为统一规范接口,属于事后兼容、亡羊补牢,解决现有代码适配问题。
✅ JDK核心落地源码:
-
InputStreamReader/OutputStreamWriter:字节流转字符流适配器,抹平字节/字符读写差异; -
java.util.Arrays#asList():数组转集合适配器,适配数组与集合接口差异; -
日期适配:新旧日期API转换适配器。
✅ Spring核心落地场景(面试高频):
-
HandlerAdapter处理器适配器(SpringMVC核心):适配不同类型处理器(Controller、HttpRequestHandler、Servlet),统一DispatcherServlet调用规范,解决不同处理器执行逻辑不一致问题; -
AdvisorAdapter:AOP通知适配器,适配不同类型Advice(前置、后置、异常通知); -
数据类型适配器:Spring类型转换体系,适配前端参数与后端实体类型差异。
✅ MyBatis落地场景 :TypeHandler 类型处理器适配器,适配数据库字段类型与Java实体类型,抹平JDBC原生类型适配缺陷。
✅ 底层源码细节:适配器实现目标统一接口,内部持有适配源对象,封装原有异构逻辑,对外输出统一方法,上层调用无需感知底层差异。
✅ 面试深挖考点:
-
适配器是被动兼容,用于已有老旧代码、第三方接口适配;
-
区别桥接模式:适配器解决现有不兼容问题 ,桥接模式事前解耦防兼容问题;
-
SpringMVC核心三大组件:处理器、处理器适配器、视图解析器,适配器是请求调度核心。
✅ 工程落地价值:无需改造老旧系统、第三方SDK,通过适配层快速对接新系统,降低改造成本。
(4)桥接模式------ 双层解耦、双向独立扩展
✅ 核心定义回顾 :拆分系统抽象层与实现层 ,解除两层硬绑定关系,让两层可以独立扩展、自由组合,从根源杜绝类爆炸问题,属于事前架构优化。
✅ JDK核心落地源码:JDBC驱动核心架构:
-
抽象层:
java.sql.Driver顶层驱动抽象接口; -
实现层:MySQL、Oracle、PostgreSQL各厂商驱动实现; 两层完全解耦,上层JDBC规范不变,底层驱动可独立替换扩展。
✅ Spring核心落地场景:
-
SLF4J日志框架:抽象层(SLF4J统一日志API)、实现层(Logback/Log4j2/JUL),双向独立扩展,无缝切换日志实现;
-
Spring消息体系:消息抽象接口、不同消息中间件(RabbitMQ/Kafka/RocketMQ)实现适配;
-
数据源适配:Spring Data JPA抽象层、不同数据库实现层。
✅ 底层源码细节:通过组合替代继承,抽象层持有实现层接口引用,而非硬绑定具体实现类,两层维度独立迭代,任意组合适配业务场景。
✅ 面试深挖考点:
-
核心解决多层维度扩展导致的类爆炸问题;
-
与适配器本质区别:桥接是事前解耦设计 ,适配器是事后兼容改造;
-
所有多维度扩展场景,优先使用桥接模式架构设计。
✅ 工程落地价值:实现框架高扩展性,底层组件可无缝替换,上层业务无需改动,完美契合开闭原则。
(5)门面模式(外观模式)------ 统一入口、简化层级
✅ 核心定义回顾 :封装底层多个复杂子系统、复杂逻辑,对外提供极简统一访问入口,屏蔽底层复杂实现,简化上层调用、降低耦合,核心是对外简化、对内封装。
✅ JDK核心落地源码 :java.lang.Math 数学工具门面类、各类工具类统一方法封装,屏蔽底层复杂计算逻辑。
✅ Spring核心落地场景(超级高频):
-
ApplicationContextSpring上下文门面:封装Bean工厂、资源加载、事件发布、环境配置、国际化、Bean注册等十余个子系统,对外仅提供统一调用入口; -
Spring工具类门面:
SpringUtils、BeanUtils封装底层复杂反射、Bean拷贝逻辑; -
分布式业务门面:统一下单接口,封装库存扣减、支付创建、物流生成、消息推送多子系统联动逻辑。
✅ Tomcat落地场景:Tomcat启动门面类,封装容器初始化、端口监听、线程启动、项目部署等复杂流程。
✅ 底层源码细节:门面类内部整合多个子系统对象,封装复杂调用链路,上层客户端仅依赖门面类,不直接依赖底层子系统,完全符合迪米特法则。
✅ 面试深挖考点:
-
门面模式是单向一对多调用,子系统之间无交互;
-
区别中介者模式:门面对外简化调用,中介者对内解耦多对多通信;
-
几乎所有框架的顶层入口类,都是门面模式的落地。
✅ 工程落地价值:简化业务调用链路,降低上层学习与使用成本,隔离底层子系统变动,提升系统稳定性。
(6)享元模式------ 对象复用、极致性能优化
✅ 核心定义回顾 :池化复用细粒度公共对象,区分内部不变状态 与外部可变状态,复用不变对象、动态替换可变状态,大幅减少对象创建数量、节约内存、提升性能。
✅ JDK核心落地源码(面试必背):
-
Integer缓存池:
Integer.valueOf()缓存-128~127整数对象,高频复用基础数字; -
String常量池:JVM全局字符串池,复用重复字符串对象,杜绝重复创建;
-
集合空对象缓存:
Collections.EMPTY_LIST空集合全局复用。
✅ Spring核心落地场景:
-
线程池、连接池:Spring线程池、数据库连接池池化复用线程、连接对象;
-
配置类缓存:全局固定配置对象、常量对象全局复用;
-
模板缓存:JdbcTemplate、RedisTemplate公共模板对象复用。
✅ MyBatis落地场景:
-
SQL语句缓存、Mapper映射缓存、结果集映射规则缓存;
-
全局配置对象复用,避免频繁创建配置实例。
✅ 底层源码细节:通过缓存容器存储不变享元对象,接收外部可变状态参数,实现同一对象复用适配不同业务场景,减少GC开销与内存占用。
✅ 面试深挖考点:
-
核心核心:区分内部不变状态、外部可变状态,是享元模式落地关键;
-
池化技术底层均基于享元思想,是高并发、高性能系统的核心优化手段;
-
杜绝大量细粒度重复对象创建,缓解JVM内存压力。
✅ 工程落地价值:极致优化内存占用,减少对象频繁创建销毁的性能损耗,适配高并发高频调用场景。
(7)组合模式------ 树形结构统一处理
✅ 核心定义回顾 :统一叶子节点(最小单元) 与**容器节点(组合单元)**的行为规范,让单个对象与组合对象拥有一致操作方式,适配所有树形层级结构的统一遍历、处理、渲染。
✅ JDK核心落地源码 :文件系统树形结构:java.io.File 文件类,统一文件(叶子)与文件夹(容器)操作,listFiles()遍历子节点,实现树形文件结构统一处理。
✅ Spring核心落地场景:
-
Spring Security权限树形节点:菜单、权限、角色层级结构,统一遍历校验;
-
系统组织架构:部门、员工树形结构,统一查询、统计、遍历;
-
Bean定义树形组装:复杂Bean依赖嵌套组合。
✅ MyBatis落地场景:
-
SQL语法树形节点:
SqlNode树形结构,统一文本节点、条件节点、循环节点的解析逻辑; -
动态SQL标签嵌套组合,统一解析渲染。
✅ 底层源码细节:定义统一树形顶层接口,叶子节点实现基础操作,容器节点聚合子节点、递归遍历处理,对外暴露完全一致的操作方法,上层无需区分节点类型。
✅ 面试深挖考点:
-
核心优势:屏蔽树形节点层级差异,统一递归处理逻辑;
-
所有树形、层级、嵌套结构场景,优先使用组合模式;
-
简化多层嵌套结构的代码逻辑,消灭层级判断冗余代码。
✅ 工程落地价值:标准化树形结构处理逻辑,大幅简化菜单、组织架构、规则嵌套、SQL嵌套等复杂层级业务代码。
6.3 行为型模式(11种)框架源码落地
(1)模板方法模式
✅ 落地场景:Spring JdbcTemplate/RedisTemplate、Spring事务模板、Servlet生命周期、MyBatis BaseExecutor
✅ 核心源码:JdbcTemplate 固化数据库操作通用流程,子类/使用者自定义差异化SQL
✅ 核心思想:父类固定通用流程,预留抽象方法实现差异化扩展,保证流程统一
(2)策略模式
✅ 落地场景:Spring 资源加载策略、限流策略、日志输出策略、Bean实例化策略、支付渠道策略
✅ 核心源码:ResourceLoader 多资源加载策略、Spring事务传播策略
✅ 核心思想:统一策略接口,动态切换不同算法,彻底消灭if-else分支
(3)观察者模式
✅ 落地场景:Spring事件驱动机制(ApplicationEvent/ApplicationListener)、Guava事件总线、MQ消息订阅
✅ 核心源码:ContextRefreshedEvent 容器刷新事件、自定义业务事件监听
✅ 核心思想:一对多发布订阅,解耦事件发布与监听,实现业务解耦联动
(4)责任链模式
✅ 落地场景:Servlet Filter过滤器链、Spring Interceptor拦截器链、Spring AOP 通知链、MyBatis 插件拦截链
✅ 核心源码:FilterChain 链式拦截、层层放行、动态终止流程
✅ 核心思想:多个处理器链式处理请求,动态增减处理器,灵活扩展拦截逻辑
(5)状态模式
✅ 落地场景:Spring Bean生命周期状态、订单/工单状态流转、事务状态、线程状态
✅ 核心源码:Bean的创建、初始化、销毁完整状态自动流转
✅ 核心思想:根据对象当前状态自动触发对应行为,规范有序流程流转
(6)命令模式
✅ 落地场景:Spring TransactionTemplate事务命令、线程池Runnable任务命令、Redis命令操作
✅ 核心源码:线程池将业务逻辑封装为Runnable命令,统一调度执行、支持重试
✅ 核心思想:封装请求为独立对象,实现请求排队、重试、撤销、日志记录
(7)迭代器模式
✅ 落地场景:JDK Collection集合迭代器、Spring BeanFactory迭代器、MyBatis结果集迭代
✅ 核心源码:Iterator 统一所有集合遍历规范,屏蔽底层存储差异
✅ 核心思想:统一容器遍历接口,适配不同数据容器,支持安全遍历删除
(8)中介者模式
✅ 落地场景:Spring MVC DispatcherServlet(中央调度器)、微服务注册中心、消息中间件Broker
✅ 核心源码:DispatcherServlet 统一转发所有请求,解耦Controller、Handler、视图解析器
✅ 核心思想:多对多复杂交互通过中介统一转发,消灭网状耦合
(9)备忘录模式
✅ 落地场景:Spring事务回滚快照、Redis数据快照、配置版本备份、MyBatis缓存快照
✅ 核心源码:事务执行前备份数据状态,异常时回滚历史快照
✅ 核心思想:无侵入保存对象状态,支持精准回滚、撤销操作
(10)访问者模式
✅ 落地场景:Java AST语法树遍历、MyBatis SQL节点解析、Spring BeanDefinition解析、代码生成器
✅ 核心源码:MyBatis SqlNode 固定结构,通过访问者实现解析、拼接、校验多维度操作
✅ 核心思想:固定数据结构,动态扩展操作行为,解耦结构与操作
(11)解释器模式
✅ 落地场景:Spring EL表达式、MyBatis OGNL表达式、规则引擎表达式、SQL解析器
✅ 核心源码:SpelExpressionParser 解析Spring自定义表达式语法
✅ 核心思想:自定义语法规则,动态解析执行表达式,实现可配置业务规则
6.4 面试终极加分总结(口述满分话术)
23种设计模式是Java框架的底层基石,创建型模式管控对象实例化,是Spring IoC容器的核心设计;结构型模式优化对象组合与架构,是框架分层、适配、解耦的核心手段;行为型模式规范对象协作与流程,是框架事件驱动、链式处理、动态扩展的底层逻辑。日常使用的Spring、MyBatis、JDK、中间件等框架,所有核心能力均基于设计模式落地,熟练掌握模式的框架源码场景,是理解框架底层、实现高阶代码设计的核心前提。
-
单例:Spring Bean 默认单例、Runtime
-
工厂方法:Spring BeanFactory、JDK 集合工厂
-
抽象工厂:Spring ApplicationContext
-
建造者:StringBuilder、MyBatis SqlSessionBuilder
-
原型:Spring 原型 Bean、Object.clone ()
-
代理:Spring AOP(JDK/CGLIB)、MyBatis Mapper 代理
-
装饰器:Java IO、Spring Cache 包装
-
模板方法:JdbcTemplate、Spring 事务模板
-
策略:Spring 资源加载策略、限流策略
-
观察者:Spring ApplicationEvent 事件
-
责任链:Spring Interceptor、Servlet Filter
-
适配器:SpringMVC HandlerAdapter
七、背诵极简思维导图框架
设计模式
├─ 设计六大原则(总约束)
├─ 创建型5种:单例、工厂方法、抽象工厂、建造者、原型
├─ 结构型7种:适配器、桥接、装饰、组合、门面、享元、代理
└─ 行为型11种:责任链/命令/迭代器/中介者/备忘录/观察者/状态/策略/模板/访问者/解释器