以下是针对Java设计模式的面试题,涵盖常见模式的定义、应用场景、代码示例及优缺点分析,适合评估候选人对设计模式的理解和实际应用能力:
1. 单例模式
题目:
- 请描述单例模式的实现方式,并说明如何实现线程安全的单例模式。
- 举一个单例模式的实际应用场景,并解释其优势。
参考答案:
实现方式:
- 饿汉式:类加载时初始化实例(线程安全,但可能存在资源浪费)
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
- 懒汉式 + 双重检查锁定(推荐):延迟加载,线程安全且高效
public class Singleton { private volatile static 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; } }
- 饿汉式:类加载时初始化实例(线程安全,但可能存在资源浪费)
应用场景:
- 数据库连接池:确保全局唯一实例,避免频繁创建/销毁连接。
- 日志记录器:统一管理日志输出,避免多个实例导致日志混乱。
优势:
- 节省内存资源,避免重复创建对象。
- 提供全局访问点,简化系统配置管理。
2. 工厂模式 vs 抽象工厂模式
题目:
- 比较工厂模式和抽象工厂模式的区别,并举例说明各自的应用场景。
- 编写一个工厂模式的代码示例,模拟不同类型的数据库连接创建。
参考答案:
区别:
工厂模式 抽象工厂模式 创建单一产品族的接口(如 Shape
)。创建多个相关产品族的接口(如 Database
和Cache
)。适用于产品种类较少的场景。 适用于需要一组相关对象的场景(如跨平台UI组件)。 工厂模式示例:
// 接口 interface Database { void connect(); } // 具体产品 class MySQL implements Database { public void connect() { System.out.println("Connected to MySQL"); } } class PostgreSQL implements Database { public void connect() { System.out.println("Connected to PostgreSQL"); } } // 工厂类 class DatabaseFactory { public static Database createDatabase(String type) { if (type.equals("MySQL")) return new MySQL(); if (type.equals("PostgreSQL")) return new PostgreSQL(); throw new IllegalArgumentException("Unknown database type"); } } // 使用 public class Main { public static void main(String[] args) { Database db = DatabaseFactory.createDatabase("MySQL"); db.connect(); } }
应用场景:
- 工厂模式: 数据库连接、日志记录器、支付方式(如支付宝、微信)。
- 抽象工厂模式: 跨平台UI框架(如Windows和Mac的按钮、文本框)。
3. 观察者模式
题目:
- 解释观察者模式的定义及其核心组件(主题、观察者),并说明其适用场景。
- 编写一个观察者模式的代码示例,模拟股票价格更新通知。
参考答案:
定义:
观察者模式定义对象间的一对多依赖关系,当主题状态变化时,所有依赖者(观察者)自动收到通知并更新。核心组件:
- Subject(主题): 维护观察者列表,提供注册/移除观察者的方法。
- Observer(观察者): 定义更新接口(如
update()
)。
代码示例:
// 观察者接口 interface StockObserver { void update(double price); } // 主题 class StockSubject { private List<StockObserver> observers = new ArrayList<>(); private double price; public void addObserver(StockObserver observer) { observers.add(observer); } public void removeObserver(StockObserver observer) { observers.remove(observer); } public void setPrice(double price) { this.price = price; notifyObservers(); } private void notifyObservers() { for (StockObserver observer : observers) { observer.update(price); } } } // 具体观察者 class Trader implements StockObserver { private String name; public Trader(String name) { this.name = name; } public void update(double price) { System.out.println(name + " received price update: " + price); } } // 使用 public class Main { public static void main(String[] args) { StockSubject stock = new StockSubject(); Trader trader1 = new Trader("Alice"); Trader trader2 = new Trader("Bob"); stock.addObserver(trader1); stock.addObserver(trader2); stock.setPrice(100.5); // 通知所有观察者 } }
适用场景:
- 事件驱动系统(如GUI事件监听)。
- 实时数据更新(如股票行情、天气预报)。
4. 策略模式
题目:
- 请说明策略模式的核心思想,并描述其与工厂模式的区别。
- 编写一个策略模式的代码示例,模拟不同支付方式(如支付宝、微信、银联)的实现。
参考答案:
核心思想:
策略模式将算法封装为独立的类,允许在运行时动态切换策略,避免硬编码条件判断。与工厂模式的区别:
- 策略模式:关注算法替换(如支付方式)。
- 工厂模式:关注对象创建(如创建数据库连接)。
代码示例:
// 策略接口 interface PaymentStrategy { void pay(double amount); } // 具体策略 class Alipay implements PaymentStrategy { public void pay(double amount) { System.out.println("Paid " + amount + " via Alipay"); } } class WeChatPay implements PaymentStrategy { public void pay(double amount) { System.out.println("Paid " + amount + " via WeChat"); } } // 上下文类 class PaymentContext { private PaymentStrategy strategy; public void setStrategy(PaymentStrategy strategy) { this.strategy = strategy; } public void executePayment(double amount) { strategy.pay(amount); } } // 使用 public class Main { public static void main(String[] args) { PaymentContext context = new PaymentContext(); context.setStrategy(new Alipay()); context.executePayment(100.0); context.setStrategy(new WeChatPay()); context.executePayment(50.0); } }
适用场景:
- 动态切换算法(如排序策略、折扣计算)。
- 避免冗长的条件判断语句(如支付方式、日志格式)。
5. 装饰器模式
题目:
- 解释装饰器模式的定义,并说明其与继承的区别。
- 编写一个装饰器模式的代码示例,模拟为咖啡添加不同配料(如牛奶、糖)。
参考答案:
定义:
装饰器模式通过组合方式动态添加对象功能,避免子类爆炸问题。与继承的区别:
- 继承:静态扩展功能,灵活性差。
- 装饰器:动态组合功能,更灵活且符合开闭原则。
代码示例:
// 抽象组件 interface Coffee { double cost(); String description(); } // 具体组件 class BlackCoffee implements Coffee { public double cost() { return 2.0; } public String description() { return "Black Coffee"; } } // 装饰器抽象类 abstract class CoffeeDecorator implements Coffee { protected Coffee decoratedCoffee; public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; } } // 具体装饰器 class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } public double cost() { return decoratedCoffee.cost() + 0.5; } public String description() { return decoratedCoffee.description() + ", Milk"; } } class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); } public double cost() { return decoratedCoffee.cost() + 0.2; } public String description() { return decoratedCoffee.description() + ", Sugar"; } } // 使用 public class Main { public static void main(String[] args) { Coffee coffee = new BlackCoffee(); coffee = new MilkDecorator(coffee); coffee = new SugarDecorator(coffee); System.out.println("Cost: " + coffee.cost()); System.out.println("Description: " + coffee.description()); } }
适用场景:
- 动态添加功能(如IO流、文本格式化)。
- 避免类继承层次过深。
6. 责任链模式
题目:
- 请描述责任链模式的核心思想,并举一个实际应用场景。
- 编写一个责任链模式的代码示例,模拟审批流程(如经理、总监、CEO)。
参考答案:
核心思想:
将请求的处理责任链式传递,避免请求发送者与处理者直接耦合。应用场景:
- 审批流程(如报销审批、权限校验)。
- 异常处理(如HTTP中间件)。
代码示例:
// 处理者接口 abstract class Approver { protected Approver nextApprover; public void setNextApprover(Approver nextApprover) { this.nextApprover = nextApprover; } public abstract void processRequest(double amount); } // 具体处理者 class Manager extends Approver { public void processRequest(double amount) { if (amount <= 1000) { System.out.println("Manager approved: " + amount); } else if (nextApprover != null) { nextApprover.processRequest(amount); } } } class Director extends Approver { public void processRequest(double amount) { if (amount <= 5000) { System.out.println("Director approved: " + amount); } else if (nextApprover != null) { nextApprover.processRequest(amount); } } } class CEO extends Approver { public void processRequest(double amount) { System.out.println("CEO approved: " + amount); } } // 使用 public class Main { public static void main(String[] args) { Approver manager = new Manager(); Approver director = new Director(); Approver ceo = new CEO(); manager.setNextApprover(director); director.setNextApprover(ceo); manager.processRequest(800); // Manager manager.processRequest(3000); // Director manager.processRequest(10000); // CEO } }
优势:
- 解耦请求发送者和处理者。
- 灵活调整责任链顺序(如增加新审批角色)。
7. 代理模式
题目:
- 解释代理模式的定义,并说明静态代理与动态代理的区别。
- 编写一个代理模式的代码示例,模拟远程服务调用的性能监控。
参考答案:
定义:
代理模式为其他对象提供一种代理以控制对这个对象的访问。静态代理 vs 动态代理:
静态代理 动态代理 手动编写代理类。 运行时动态生成代理类(如JDK Proxy)。 适用于固定接口。 适用于任意接口。 代码示例:
// 接口 interface Service { void execute(); } // 实现类 class RealService implements Service { public void execute() { System.out.println("Executing service..."); try { Thread.sleep(1000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } } } // 代理类 class ServiceProxy implements Service { private Service realService; public ServiceProxy() { this.realService = new RealService(); } public void execute() { long startTime = System.currentTimeMillis(); realService.execute(); long endTime = System.currentTimeMillis(); System.out.println("Execution time: " + (endTime - startTime) + "ms"); } } // 使用 public class Main { public static void main(String[] args) { Service service = new ServiceProxy(); service.execute(); } }
适用场景:
- 远程调用(如RPC)。
- 权限控制、日志记录、缓存等。
8. 模板方法模式
题目:
- 请描述模板方法模式的核心思想,并编写一个代码示例,模拟不同饮料的制作流程。
参考答案:
核心思想:
定义算法骨架,将某些步骤延迟到子类实现,避免代码重复。代码示例:
// 抽象类 abstract class Beverage { // 模板方法(final防止覆盖) public final void prepareRecipe() { boilWater(); brew(); pourInCup(); addCondiments(); } // 公共步骤 private void boilWater() { System.out.println("Boiling water"); } protected abstract void brew(); private void pourInCup() { System.out.println("Pouring into cup"); } protected abstract void addCondiments(); // 钩子方法(可选覆盖) public boolean customerWantsCondiments() { return true; } } // 具体类 class Coffee extends Beverage { protected void brew() { System.out.println("Brewing coffee"); } protected void addCondiments() { if (customerWantsCondiments()) { System.out.println("Adding sugar and milk"); } } public boolean customerWantsCondiments() { return false; // 用户不想要糖和奶 } } class Tea extends Beverage { protected void brew() { System.out.println("Steeping tea"); } protected void addCondiments() { System.out.println("Adding lemon"); } } // 使用 public class Main { public static void main(String[] args) { Beverage coffee = new Coffee(); coffee.prepareRecipe(); Beverage tea = new Tea(); tea.prepareRecipe(); } }
适用场景:
- 多步骤算法(如编译器、测试框架)。
- 避免重复代码(如日志记录、事务管理)。
9. 适配器模式
题目:
- 解释适配器模式的定义,并说明其与装饰器模式的区别。
- 编写一个适配器模式的代码示例,模拟旧接口与新接口的兼容。
参考答案:
定义:
适配器模式将一个类的接口转换为客户期望的另一个接口,使原本不兼容的类可以协同工作。与装饰器模式的区别:
- 适配器:适配接口(解决兼容性问题)。
- 装饰器:增强功能(不改变接口,仅扩展行为)。
代码示例:
// 旧接口 interface OldService { void oldMethod(); } // 新接口 interface NewService { void newMethod(); } // 适配器类 class Adapter implements NewService { private OldService oldService; public Adapter(OldService oldService) { this.oldService = oldService; } public void newMethod() { oldService.oldMethod(); // 适配旧接口 } } // 使用 public class Main { public static void main(String[] args) { OldService old = new OldService() { public void oldMethod() { System.out.println("Old method called"); } }; NewService newService = new Adapter(old); newService.newMethod(); // 输出 "Old method called" } }
适用场景:
- 集成第三方库(如旧系统对接新API)。
- 适配遗留代码。
10. 备忘录模式
题目:
- 请描述备忘录模式的定义,并编写一个代码示例,模拟游戏存档功能。
参考答案:
定义:
备忘录模式用于保存对象的内部状态,并在需要时恢复状态(撤销/重做)。代码示例:
// 原发器类 class Game { private int level; private int score; public Game(int level, int score) { this.level = level; this.score = score; } public Memento save() { return new Memento(level, score); } public void restore(Memento memento) { this.level = memento.getLevel(); this.score = memento.getScore(); } public void play() { level++; score += 10; } public void display() { System.out.println("Level: " + level + ", Score: " + score); } } // 备忘录类 class Memento { private final int level; private final int score; public Memento(int level, int score) { this.level = level; this.score = score; } public int getLevel() { return level; } public int getScore() { return score; } } // 使用 public class Main { public static void main(String[] args) { Game game = new Game(1, 0); game.play(); game.display(); // Level 2, Score 10 Memento saved = game.save(); game.play(); game.display(); // Level 3, Score 20 game.restore(saved); game.display(); // 恢复到 Level 2, Score 10 } }
适用场景:
- 游戏存档、编辑器撤销操作。
- 事务回滚、数据库快照。
总结
以上题目覆盖了Java设计模式的核心知识点,包括创建型、结构型、行为型模式的应用。通过这些问题,可以全面评估候选人对设计模式的理解深度、实际编码能力以及对面向对象设计原则(如开闭原则、单一职责原则)的掌握程度。