1. 单例模式:如何用 volatilesynchronized 实现?(双重校验锁)

这是面试中最标准的单例模式写法(DCL - Double Checked Locking)。

代码逻辑:

public class Singleton {
    // 1. volatile 关键字:禁止指令重排
    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;
    }
}

面试回答重点:

  • 为什么要有两次 if (instance == null)

    • 第一次是为了性能(一旦实例创建好,后续线程就不用排队加锁了)。

    • 第二次是为了安全(防止两个线程同时通过了第一层检查,一个在等待锁,另一个创建完释放锁后,等待的那个进去又创建了一次)。

  • 为什么要用 volatile?(核心考点)

    • instance = new Singleton() 这行代码在 JVM 里其实分三步:

      1. 分配内存空间。

      2. 初始化对象。

      3. 将引用指向内存地址。

    • 如果不加 volatile,CPU 可能会指令重排(变成 1 3 2)。

    • 后果: 线程 A 执行了 1 和 3(此时 instance 已经不为 null 了,但对象还没初始化),线程 B 进来发现 instance != null,直接拿去用了,结果用到的是个半成品,导致报错。


2. 代理模式 vs 适配器模式的区别?

这两个模式结构很像(都是包一层),但意图完全不同。

  • 适配器模式 (Adapter Pattern):

    • 核心意图: “兼容”。把一个类的接口变换成客户端所期待的另一种接口。

    • 比喻: 手机转接头(把 Type-C 转成 3.5mm 耳机孔)。

    • 特点: 改变了原来的接口。

  • 代理模式 (Proxy Pattern):

    • 核心意图: “控制”与“增强”。在不改变接口的前提下,对原对象的功能进行扩展(如加日志、加权限控制)。

    • 比喻: 明星的经纪人。你要找明星(原对象)谈生意,必须先经过经纪人(代理),经纪人负责谈价钱(增强逻辑),谈好了再让明星签字。

    • 特点: 接口保持一致。

总结话术: “适配器模式是为了解决接口不匹配的问题(比如改电压);代理模式是为了在不改变接口的前提下增强功能(比如 AOP 切面)。”


3. 责任链模式 (Chain of Responsibility) 的应用场景?

核心概念: 将请求的发送者和接收者解耦。请求沿着一条链传递,直到有一个对象处理它为止,或者每个对象都处理一部分。

面试常考应用场景:

  1. Java Web 过滤器链 (Filter Chain): 请求到达 Servlet 之前,先经过编码过滤、登录检查过滤等。

  2. Spring 拦截器 (Interceptor): Controller 执行前后的处理。

  3. 日志级别 (Logger): debug info error。如果不满足级别就传给下一个,或者冒泡处理。

  4. 风控系统/审批流程: 比如请假审批:组长 经理 总监。金额小的时候组长批完就结束,金额大了继续往下传。

模式核心关键词典型用途
单例模式volatile, 双重检查保证全局只有一个实例 (如数据库连接池)
适配器模式兼容救火代码,连接旧接口
代理模式增强Spring AOP, RPC, 日志, 事务
责任链模式链式处理Filter, 拦截器, 审批流