资深Java工程师的面试题目(三)设计模式

发布于:2025-06-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

以下是针对Java设计模式的面试题,涵盖常见模式的定义、应用场景、代码示例及优缺点分析,适合评估候选人对设计模式的理解和实际应用能力:


1. 单例模式

题目:

  • 请描述单例模式的实现方式,并说明如何实现线程安全的单例模式。
  • 举一个单例模式的实际应用场景,并解释其优势。

参考答案:

  • 实现方式:

    1. 饿汉式:类加载时初始化实例(线程安全,但可能存在资源浪费)
      public class Singleton {
          private static final Singleton INSTANCE = new Singleton();
          private Singleton() {}
          public static Singleton getInstance() {
              return INSTANCE;
          }
      }
      
    2. 懒汉式 + 双重检查锁定(推荐):延迟加载,线程安全且高效
      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;
          }
      }
      
    3. 静态内部类:利用类加载机制保证线程安全
      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)。 创建多个相关产品族的接口(如DatabaseCache)。
    适用于产品种类较少的场景。 适用于需要一组相关对象的场景(如跨平台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设计模式的核心知识点,包括创建型、结构型、行为型模式的应用。通过这些问题,可以全面评估候选人对设计模式的理解深度、实际编码能力以及对面向对象设计原则(如开闭原则、单一职责原则)的掌握程度。


网站公告

今日签到

点亮在社区的每一天
去签到