Java常见设计模式面试题(高频)

单例模式(Singleton Pattern)

  • 如何保证线程安全?
  • 双重检查锁(DCL)与静态内部类实现的区别?
  • 单例模式的优缺点及应用场景。

1. 什么是单例模式

单例模式(Singleton Pattern) 是一种创建型设计模式,它的核心思想是:

一个类在整个系统中只能有一个实例,并且提供一个全局访问点来获取它。

换句话说,单例模式保证了:

  1. 唯一性:系统中只有一个该类的对象。
  2. 全局访问:提供一个静态方法获取这个唯一对象。
  3. 延迟加载(可选):在第一次使用时才创建对象。

2. 原理

单例模式的原理可以用三句话概括:

  1. 构造方法私有化 → 防止外部通过 new 创建对象。
  2. 在类内部创建唯一实例 → 通过静态变量保存。
  3. 提供公共静态方法获取实例 → 外部只能通过这个方法访问对象。

3. 流程图

单例模式的执行流程如下:

复制代码
外部调用 getInstance()
        ↓
判断实例是否已创建?
        ↓
   否 → 创建实例
        ↓
   是 → 直接返回实例

4. 单例模式的实现方式

4.1 饿汉式(线程安全,类加载时创建)

java 复制代码
public class Singleton {
    // 1. 在类加载时就创建唯一实例
    private static final Singleton instance = new Singleton();

    // 2. 构造方法私有化
    private Singleton() {}

    // 3. 提供全局访问点
    public static Singleton getInstance() {
        return instance;
    }
}

特点

  • 优点:实现简单,线程安全(因为类加载时就创建了实例)。
  • 缺点:类加载时就创建对象,可能会浪费内存(如果一直没用到)。

4.2 懒汉式(延迟加载,线程不安全)

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) { // 第一次调用时创建
            instance = new Singleton();
        }
        return instance;
    }
}

特点

  • 优点:第一次使用时才创建对象,节省内存。
  • 缺点:线程不安全,多线程环境下可能会创建多个实例。

4.3 懒汉式 + 线程安全(同步方法)

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

特点

  • 优点:线程安全。
  • 缺点:synchronized 会影响性能(每次调用都要加锁)。

4.4 双重检查锁(DCL,推荐)

java 复制代码
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

特点

  • 优点:线程安全,性能好(只在第一次创建时加锁)。
  • 关键点:volatile 防止指令重排。

4.5 静态内部类(推荐)

java 复制代码
public class Singleton {
    private Singleton() {}

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

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

特点

  • 优点:线程安全,延迟加载,性能好。
  • 原理:利用类加载机制,Holder 类只有在第一次调用 getInstance() 时才会被加载。

5. 使用场景

单例模式适用于:

  • 配置类(全局唯一配置对象)
  • 日志管理器(全局统一日志记录)
  • 线程池(全局唯一线程池对象)
  • 数据库连接池(全局唯一连接池)
  • 缓存管理器(全局唯一缓存对象)

6. 优缺点

优点

  • 节省内存(只创建一个实例)
  • 提供全局访问点
  • 控制资源访问(避免多个实例导致冲突)

缺点

  • 单例对象一旦创建,生命周期和应用程序一致,可能占用内存。
  • 在多线程环境下实现不当会导致线程安全问题。
  • 可能违反单一职责原则(既负责业务逻辑,又负责实例控制)。

7. 示例:日志管理器(静态内部类实现)

java 复制代码
public class Logger {
    private Logger() {}

    private static class LoggerHolder {
        private static final Logger INSTANCE = new Logger();
    }

    public static Logger getInstance() {
        return LoggerHolder.INSTANCE;
    }

    public void log(String message) {
        System.out.println("[LOG] " + message);
    }

    public static void main(String[] args) {
        Logger logger1 = Logger.getInstance();
        Logger logger2 = Logger.getInstance();

        logger1.log("系统启动");
        logger2.log("用户登录");

        System.out.println(logger1 == logger2); // true,说明是同一个实例
    }
}

运行结果

复制代码
[LOG] 系统启动
[LOG] 用户登录
true

说明 logger1logger2 是同一个对象。


总结

  • 单例模式的核心是构造方法私有化 + 静态实例 + 全局访问方法
  • 推荐使用静态内部类或**双重检查锁(DCL)**实现,既安全又高效。

工厂模式(Factory Pattern)

  • 简单工厂、工厂方法、抽象工厂的区别?
  • 在什么场景下选择哪种工厂模式?

1. 什么是工厂模式

工厂模式(Factory Pattern) 是一种创建型设计模式,它的核心思想是:

将对象的创建过程封装起来,通过工厂类来统一管理和创建对象,而不是在代码中直接使用 new

这样做的好处是:

  • 解耦:调用者不需要关心对象的具体创建过程。
  • 可扩展:新增产品类型时,只需要修改工厂类,不影响调用者。

2. 原理

工厂模式的原理可以用三句话概括:

  1. 定义一个工厂类,负责创建对象。
  2. 调用者通过工厂类获取对象 ,而不是直接 new
  3. 工厂类根据条件返回不同的对象实例

3. 工厂模式的类型

工厂模式有三种常见实现方式:

3.1 简单工厂模式(Simple Factory)

  • 一个工厂类,根据传入的参数决定创建哪种对象。
  • 缺点:不符合开闭原则(新增产品需要修改工厂类)。

3.2 工厂方法模式(Factory Method)

  • 定义一个抽象工厂接口,每个具体工厂负责创建一种产品。
  • 优点:符合开闭原则,新增产品只需新增工厂类。

3.3 抽象工厂模式(Abstract Factory)

  • 提供一个接口,用于创建一系列相关或依赖的对象。
  • 优点:可以创建多个产品族,适合复杂系统。

4. 流程图(以简单工厂为例)

复制代码
调用者
   ↓
调用工厂类的 createProduct(type)
   ↓
工厂类判断 type
   ↓
创建对应的产品对象
   ↓
返回给调用者

5. Java 示例

5.1 简单工厂模式

java 复制代码
// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ProductA implements Product {
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
class ProductB implements Product {
    public void use() {
        System.out.println("使用产品B");
    }
}

// 工厂类
class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equalsIgnoreCase(type)) {
            return new ProductA();
        } else if ("B".equalsIgnoreCase(type)) {
            return new ProductB();
        }
        throw new IllegalArgumentException("未知产品类型: " + type);
    }
}

// 测试
public class FactoryDemo {
    public static void main(String[] args) {
        Product p1 = SimpleFactory.createProduct("A");
        p1.use();

        Product p2 = SimpleFactory.createProduct("B");
        p2.use();
    }
}

运行结果

复制代码
使用产品A
使用产品B

5.2 工厂方法模式

java 复制代码
// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ProductA implements Product {
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
class ProductB implements Product {
    public void use() {
        System.out.println("使用产品B");
    }
}

// 工厂接口
interface Factory {
    Product createProduct();
}

// 工厂A
class FactoryA implements Factory {
    public Product createProduct() {
        return new ProductA();
    }
}

// 工厂B
class FactoryB implements Factory {
    public Product createProduct() {
        return new ProductB();
    }
}

// 测试
public class FactoryMethodDemo {
    public static void main(String[] args) {
        Factory factoryA = new FactoryA();
        Product p1 = factoryA.createProduct();
        p1.use();

        Factory factoryB = new FactoryB();
        Product p2 = factoryB.createProduct();
        p2.use();
    }
}

运行结果

复制代码
使用产品A
使用产品B

6. 使用场景

工厂模式适用于:

  • 对象创建过程复杂(需要很多配置或依赖)。
  • 系统需要根据条件创建不同类型的对象
  • 希望解耦对象的创建和使用
  • 需要统一管理对象的创建(比如日志、数据库连接、缓存等)。

7. 优缺点

优点

  • 解耦:调用者不关心对象的创建细节。
  • 可维护:统一管理对象创建。
  • 可扩展:新增产品类型时,修改或新增工厂类即可。

缺点

  • 简单工厂不符合开闭原则。
  • 工厂方法和抽象工厂会增加类的数量,结构更复杂。

8. 总结

  • 简单工厂:一个工厂类创建多种产品,结构简单,但扩展性差。
  • 工厂方法:每个工厂类只创建一种产品,扩展性好,符合开闭原则。
  • 抽象工厂:创建多个产品族,适合复杂系统。

策略模式(Strategy Pattern)

  • 如何避免大量 if-elseswitch
  • 在支付、排序、日志等场景中的应用。

1. 什么是策略模式

策略模式(Strategy Pattern) 是一种行为型设计模式,它的核心思想是:

定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式让算法的变化不会影响使用算法的客户。

简单来说:

  • 有多个可替换的算法(策略)。
  • 客户端可以在运行时选择使用哪一个算法。
  • 算法的实现和使用分离,方便扩展。

2. 原理

策略模式的原理可以用三句话概括:

  1. 定义一个策略接口,声明算法方法。
  2. 实现多个策略类,每个类封装一种算法。
  3. 在上下文类中持有策略对象,并在运行时选择具体策略。

3. 流程图

复制代码
客户端
   ↓
选择具体策略(StrategyA / StrategyB / StrategyC)
   ↓
将策略对象传给上下文(Context)
   ↓
上下文调用策略对象的方法
   ↓
执行对应的算法

4. Java 示例

4.1 定义策略接口

java 复制代码
// 策略接口
interface PaymentStrategy {
    void pay(int amount);
}

4.2 实现具体策略类

java 复制代码
// 支付宝支付策略
class AlipayStrategy implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("使用支付宝支付 " + amount + " 元");
    }
}

// 微信支付策略
class WeChatStrategy implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("使用微信支付 " + amount + " 元");
    }
}

// 银行卡支付策略
class BankCardStrategy implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("使用银行卡支付 " + amount + " 元");
    }
}

4.3 上下文类

java 复制代码
// 上下文类
class PaymentContext {
    private PaymentStrategy strategy;

    // 构造方法注入策略
    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePayment(int amount) {
        strategy.pay(amount);
    }
}

4.4 测试

java 复制代码
public class StrategyPatternDemo {
    public static void main(String[] args) {
        // 使用支付宝支付
        PaymentContext context1 = new PaymentContext(new AlipayStrategy());
        context1.executePayment(100);

        // 使用微信支付
        PaymentContext context2 = new PaymentContext(new WeChatStrategy());
        context2.executePayment(200);

        // 使用银行卡支付
        PaymentContext context3 = new PaymentContext(new BankCardStrategy());
        context3.executePayment(300);
    }
}

运行结果

复制代码
使用支付宝支付 100 元
使用微信支付 200 元
使用银行卡支付 300 元

5. 使用场景

策略模式适用于:

  • 系统中有多个算法可以替换(比如不同的排序方法、不同的支付方式)。
  • 需要在运行时动态选择算法。
  • 避免使用大量的 if-elseswitch 判断。

6. 优缺点

优点

  • 算法和使用分离,符合开闭原则。
  • 可扩展性好,新增策略只需实现接口,不影响原有代码。
  • 避免大量条件判断

缺点

  • 客户端必须知道所有策略类,并自行选择合适的策略。
  • 策略类数量可能会增加。

7. 总结

  • 策略模式的核心:把算法封装成独立的类,通过接口统一管理。
  • 关键点:上下文类持有策略对象,运行时可以替换。
  • 适合场景:多种可替换的算法、运行时动态选择。

观察者模式(Observer Pattern)

  • JDK中 java.util.Observer 的使用。
  • 如何实现事件驱动机制?

1. 什么是观察者模式

观察者模式(Observer Pattern) 是一种行为型设计模式,它的核心思想是:

定义对象间的一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会收到通知并自动更新。

简单来说:

  • 一个主题(Subject) ,它的状态变化会通知多个观察者(Observer)
  • 观察者可以订阅取消订阅主题。
  • 主题和观察者之间是松耦合的。

2. 原理

观察者模式的原理可以用三句话概括:

  1. **主题(Subject)**维护一个观察者列表。
  2. 当主题状态变化时,调用所有观察者的更新方法
  3. 观察者可以随时注册或取消注册。

3. 流程图

复制代码
主题(Subject)
   ↑ 注册/取消订阅
观察者(Observer)列表
   ↓
主题状态变化 → 通知所有观察者 → 观察者执行更新逻辑

4. Java 示例

4.1 定义观察者接口

java 复制代码
// 观察者接口
interface Observer {
    void update(String message);
}

4.2 定义主题接口

java 复制代码
// 主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(String message);
}

4.3 实现具体主题

java 复制代码
import java.util.ArrayList;
import java.util.List;

class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

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

    // 主题状态变化时调用
    public void changeState(String message) {
        System.out.println("主题状态变化: " + message);
        notifyObservers(message);
    }
}

4.4 实现具体观察者

java 复制代码
class ConcreteObserverA implements Observer {
    @Override
    public void update(String message) {
        System.out.println("观察者A收到通知: " + message);
    }
}

class ConcreteObserverB implements Observer {
    @Override
    public void update(String message) {
        System.out.println("观察者B收到通知: " + message);
    }
}

4.5 测试

java 复制代码
public class ObserverPatternDemo {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        Observer observerA = new ConcreteObserverA();
        Observer observerB = new ConcreteObserverB();

        // 注册观察者
        subject.registerObserver(observerA);
        subject.registerObserver(observerB);

        // 状态变化
        subject.changeState("版本1.0发布");

        // 移除一个观察者
        subject.removeObserver(observerA);

        // 再次状态变化
        subject.changeState("版本2.0发布");
    }
}

运行结果

复制代码
主题状态变化: 版本1.0发布
观察者A收到通知: 版本1.0发布
观察者B收到通知: 版本1.0发布
主题状态变化: 版本2.0发布
观察者B收到通知: 版本2.0发布

5. 使用场景

观察者模式适用于:

  • 事件监听机制(GUI按钮点击、鼠标事件等)。
  • 消息订阅/发布系统(如新闻推送、微信公众号)。
  • 数据变化通知(如股票价格变化通知投资者)。
  • 分布式系统中的事件驱动架构

6. 优缺点

优点

  • 松耦合:主题和观察者之间只依赖接口,不依赖具体实现。
  • 可扩展性好:可以随时增加或移除观察者。
  • 符合开闭原则:新增观察者不影响主题代码。

缺点

  • 如果观察者数量很多,通知会很耗时。
  • 可能出现循环依赖,导致无限通知。
  • 观察者收到通知的顺序不可控。

7. 总结

  • 核心思想:主题维护观察者列表,状态变化时通知所有观察者。
  • 关键点:解耦主题和观察者,方便扩展。
  • 常见应用:事件监听、消息推送、数据变化通知。

模板方法模式(Template Method Pattern)

  • 抽象类与具体实现类的关系。
  • 在Spring框架中的应用案例。

1. 什么是模板方法模式

模板方法模式(Template Method Pattern) 是一种行为型设计模式,它的核心思想是:

在一个抽象类中定义算法的骨架(流程),并将某些步骤的具体实现延迟到子类中。子类可以在不改变算法结构的情况下,重新定义某些步骤的实现。

简单来说:

  • 抽象类定义固定的执行流程(模板方法)。
  • 流程中的某些步骤由子类实现。
  • 保证算法结构不变,但允许子类定制部分行为。

2. 原理

模板方法模式的原理可以用三句话概括:

  1. 抽象类定义模板方法(final修饰,防止被子类修改流程)。
  2. 模板方法中调用抽象方法钩子方法
  3. 子类实现这些抽象方法,完成具体步骤。

3. 流程图

复制代码
抽象类(AbstractClass)
   ↓
模板方法(固定流程)
   ↓
调用抽象方法(由子类实现)
   ↓
子类(ConcreteClass)实现具体步骤

4. Java 示例

4.1 抽象类定义模板方法

java 复制代码
abstract class DataProcessor {
    // 模板方法(固定流程)
    public final void process() {
        readData();
        processData();
        saveData();
    }

    // 抽象方法(由子类实现)
    protected abstract void readData();
    protected abstract void processData();

    // 具体方法(可选,子类可继承)
    protected void saveData() {
        System.out.println("保存数据到数据库");
    }
}

4.2 子类实现具体步骤

java 复制代码
class CSVDataProcessor extends DataProcessor {
    @Override
    protected void readData() {
        System.out.println("读取 CSV 文件数据");
    }

    @Override
    protected void processData() {
        System.out.println("处理 CSV 数据");
    }
}

class ExcelDataProcessor extends DataProcessor {
    @Override
    protected void readData() {
        System.out.println("读取 Excel 文件数据");
    }

    @Override
    protected void processData() {
        System.out.println("处理 Excel 数据");
    }
}

4.3 测试

java 复制代码
public class TemplateMethodDemo {
    public static void main(String[] args) {
        DataProcessor csvProcessor = new CSVDataProcessor();
        csvProcessor.process();

        System.out.println("----------------");

        DataProcessor excelProcessor = new ExcelDataProcessor();
        excelProcessor.process();
    }
}

运行结果

复制代码
读取 CSV 文件数据
处理 CSV 数据
保存数据到数据库
----------------
读取 Excel 文件数据
处理 Excel 数据
保存数据到数据库

5. 使用场景

模板方法模式适用于:

  • 多个类有相同的处理流程,但某些步骤不同
  • 希望复用流程代码,减少重复
  • 需要保证算法结构不变,但允许子类定制部分行为

常见应用:

  • 数据处理(不同数据源但处理流程一致)。
  • 游戏开发(不同关卡的流程一致,但细节不同)。
  • Web框架中的请求处理(如 Spring 的 doGet / doPost)。

6. 优缺点

优点

  • 代码复用:公共流程在抽象类中实现,减少重复代码。
  • 结构稳定:模板方法固定流程,保证算法结构不变。
  • 易扩展:新增子类即可定制不同的步骤。

缺点

  • 继承限制:模板方法依赖继承,灵活性不如组合模式。
  • 流程固定:如果流程变化,需要修改抽象类,可能影响所有子类。

7. 总结

  • 核心思想:抽象类定义固定流程,子类实现具体步骤。
  • 关键点 :模板方法用 final 修饰,防止子类修改流程。
  • 适合场景:多个类有相同的流程结构,但部分步骤不同。
相关推荐
老鼠只爱大米2 小时前
Java设计模式之建造者模式(Builder)详解
java·设计模式·建造者模式·builder·23种设计模式
笃行客从不躺平2 小时前
线程池原理复习
java·开发语言
weixin_448771722 小时前
SpringMVC执行流程源码分析之二
java
A尘埃2 小时前
大模型应用python+Java后端+Vue前端的整合
java·前端·python
A尘埃2 小时前
LLM大模型评估攻略
开发语言·python
littlepeanut.top2 小时前
C++中将FlatBuffers序列化为JSON
开发语言·c++·json·flatbuffers
一晌小贪欢3 小时前
【Python办公】处理 CSV和Excel 文件操作指南
开发语言·python·excel·excel操作·python办公·csv操作
清风与日月3 小时前
c# 集成激光雷达(以思岚A1为例)
开发语言·c#
皮皮林5513 小时前
MinIO 不再“开放”,RustFS 能否成为更优选择?
java