🎯 网友最关心的10个设计模式面试问题
⑴ 什么是设计模式?为什么Java面试中频繁考察设计模式?
设计模式是解决软件开发中常见问题的可复用方案,是经过验证的最佳实践总结。Java作为面向对象编程语言,其核心思想与设计模式高度契合。在面试中,考察设计模式不仅检验候选人对OOP原则(如单一职责、开闭原则)的理解深度,还考察其在真实场景中抽象建模、解耦设计与扩展能力的综合素养。例如:阿里巴巴、腾讯等大厂常将设计模式作为中高级工程师岗位的必考项。
// 设计模式体现的核心思想示例
public interface PaymentStrategy {
void pay(double amount);
}
public class WechatPay implements PaymentStrategy {
public void pay(double amount) { }
}
public class Alipay implements PaymentStrategy {
public void pay(double amount) { }
}
// 客户端无需关心具体实现,仅依赖接口
public class CheckoutService {
private PaymentStrategy strategy;
public CheckoutService(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void checkout(double amount) {
strategy.pay(amount); // 多态 + 策略模式
}
}
⚡ 面试官常追问:你能举一个工作中用到策略模式的实例吗?请说明如何避免if-else滥用。
⑵ 单例模式有哪几种写法?线程安全如何保证?
常见实现方式包括:懒汉式(线程不安全→加同步锁→双重检查锁定→静态内部类→枚举)、饿汉式、注册式(如Spring容器管理)。其中,双重检查锁定(DCL)需注意`volatile`防止指令重排;静态内部类利用JVM类加载机制保证线程安全且延迟加载;枚举方式最简洁且天然防反射、序列化破坏。
// 双重检查锁定(DCL)+ volatile
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
// 静态内部类(推荐)
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
// 枚举方式(最安全)
public enum SingletonEnum {
INSTANCE;
public void doSomething() { }
}
⚙️ 注意:Spring默认单例是基于容器作用域的,非JVM级单例;若需全局唯一,需结合注册中心或分布式锁。
⑶ 工厂模式与抽象工厂模式有何区别?Spring中如何使用?
工厂模式(Simple Factory)是创建型模式的简化版,由一个工厂类根据参数返回不同产品;抽象工厂模式则提供一个创建一系列相关或相互依赖对象的接口,无需指定具体类。Spring中`BeanFactory`与`ApplicationContext`是工厂模式的典型应用;`FactoryBean`接口允许自定义复杂对象的创建逻辑,如MyBatis的`SqlSessionFactoryBean`。
// 简单工厂(非23种标准模式,但常用)
public class CarFactory {
public static Car createCar(String type) {
if ("Tesla".equals(type)) return new Tesla();
if ("BYD".equals(type)) return new BYD();
return null;
}
}
// 抽象工厂(产品族)
public interface CarFactory {
Engine createEngine();
Tire createTire();
}
public class TeslaFactory implements CarFactory {
public Engine createEngine() { return new TeslaEngine(); }
public Tire createTire() { return new TeslaTire(); }
}
// Spring中的FactoryBean示例
public class MyFactoryBean implements FactoryBean {
public MyService getObject() { return new MyServiceImpl(); }
public Class> getObjectType() { return MyService.class; }
}
⚠️ 易错点:Spring的`@Component`默认是单例,若需多例需配置`@Scope("prototype")`。
⑷ 观察者模式在JDK中的实现有哪些?如何替代Guava EventBus?
JDK原生提供`java.util.Observer`与`Observable`,但已过时(线程不安全、继承限制)。现代方案推荐使用`java.beans.PropertyChangeListener`或第三方库(如Guava EventBus、Reactor)。在Spring中,`ApplicationEventPublisher`是观察者模式的封装,配合`@EventListener`可实现事件驱动架构。
// Spring事件驱动示例
@Data
public class OrderCreatedEvent extends ApplicationEvent {
private final String orderId;
public OrderCreatedEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
}
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(String orderId) {
// 业务逻辑...
publisher.publishEvent(new OrderCreatedEvent(this, orderId));
}
}
@Component
public class OrderEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("订单创建成功:" + event.getOrderId());
// 发送短信/库存扣减等异步操作
}
}
⚡ 高频追问:如何保证事件消费的可靠性?需引入重试机制与死信队列。
⑸ 策略模式与状态模式有何本质区别?Spring中如何应用?
策略模式(Strategy)聚焦于算法族的封装与切换,客户端决定使用哪个策略;状态模式(State)则允许对象在其内部状态改变时改变行为,状态切换由对象内部自动管理。Spring中`HandlerMapping`接口的多种实现(如`RequestMappingHandlerMapping`)体现策略模式;`StateMachine`框架(如Spring State Machine)则实现状态模式。
// 策略模式:支付方式切换
public class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void pay(double amount) {
strategy.pay(amount);
}
}
// 状态模式:订单状态流转
public abstract class OrderState {
protected OrderContext context;
public abstract void approve();
public abstract void reject();
}
public class PendingState extends OrderState {
public void approve() {
context.setState(new ApprovedState());
}
public void reject() {
context.setState(new RejectedState());
}
}
📌 实战建议:若状态流转固定且复杂(如审批流),优先选状态模式;若仅需动态切换算法(如促销规则),策略模式更轻量。
⑹ 模板方法模式在MyBatis中的应用有哪些?
MyBatis的`BaseExecutor`是模板方法模式的典型应用,其中`query()`方法定义了查询的骨架流程(缓存检查→SQL执行→结果处理),子类(如`SimpleExecutor`、`BatchExecutor`)仅需重写具体步骤(如`doQuery()`)。这种设计实现了“好莱坞原则”——“别找我,我找你”。
// 模板方法抽象类
public abstract class BaseSqlExecutor {
public List executeQuery(String sql, Object param) {
preHandle(); // 钩子方法
ResultSet rs = doQuery(sql, param); // 抽象方法,子类实现
List results = postHandle(rs); // 钩子方法
return results;
}
protected void preHandle() {}
protected List postHandle(ResultSet rs) { return new ArrayList<>(); }
protected abstract ResultSet doQuery(String sql, Object param) throws SQLException;
}
// 具体子类
public class SimpleExecutor extends BaseSqlExecutor {
protected ResultSet doQuery(String sql, Object param) throws SQLException {
Statement stmt = connection.createStatement();
return stmt.executeQuery(sql);
}
}
⚙️ 面试加分点:可提及JDK中`AbstractList`、`AbstractMap`等集合框架也使用模板方法。
⑺ 适配器模式与装饰器模式的区别?MyBatis中如何体现?
适配器(Adapter)用于将一个接口转换为客户端期望的接口,解决接口不兼容问题;装饰器(Decorator)则动态给对象添加新功能,不改变其结构。MyBatis中`Cursor`接口通过适配器模式兼容`Iterator`;`SqlSessionTemplate`是`SqlSession`的装饰器,通过代理增强事务与异常处理能力。
// 适配器模式:Cursor适配Iterator
public interface Cursor extends Iterable, Closeable {
Iterator iterator();
}
// 装饰器模式:SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession {
private final SqlSession sqlSessionProxy;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor(sqlSessionFactory)
);
}
// 所有方法委托给代理对象处理
}
📌 易混淆点:装饰器模式强调“增强”,适配器强调“转换”;装饰器通常包装自身类型,适配器可包装不同接口。
⑻ 责任链模式在Spring MVC中的执行流程?
Spring MVC的`HandlerInterceptor`链是责任链模式的典型应用。请求进入`DispatcherServlet`后,按顺序执行`preHandle()`→`Handler`→`postHandle()`→`afterCompletion()`。每个拦截器可决定是否放行(返回true),形成链式处理。
// 自定义拦截器
public class AuthInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
response.sendError(401, "Unauthorized");
return false;
}
return true;
}
public void postHandle(...) { }
public void afterCompletion(...) { }
}
// 配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login");
}
}
⚡ 面试追问:如何实现异步请求下的责任链?需配合`Callable`与`DeferredResult`。
⑼ 代理模式在Spring AOP中的实现原理?JDK动态代理与CGLIB的区别?
Spring AOP默认对实现接口的类使用JDK动态代理(基于`InvocationHandler`),对无接口的类使用CGLIB(通过继承生成子类)。JDK代理要求目标类实现接口,CGLIB通过字节码增强,但无法代理`final`类与方法。
// JDK动态代理示例
public class LoggingInvocationHandler implements InvocationHandler {
private Object target;
public LoggingInvocationHandler(Object target) { this.target = target; }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用方法:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("方法返回:" + result);
return result;
}
}
// CGLIB代理示例(需引入asm依赖)
public class LoggingMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
System.out.println("前置通知");
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置通知");
return result;
}
}
⚙️ 关键区别:JDK代理基于接口,性能略优;CGLIB基于继承,适用范围更广但无法代理final方法。
⑽ 设计模式学习路径建议?如何避免死记硬背?
推荐“三步法”:① 理解动机(解决什么问题)→② 掌握UML图与代码结构→③ 结合实际项目重构。例如:用策略模式重构if-else后,代码可读性提升40%,测试覆盖率提高25%。建议从高频模式(单例、工厂、观察者、策略)入手,结合Spring源码阅读深化理解。
📚 推荐学习资料:《Head First设计模式》《Java与模式》《Spring源码深度解析》