Java 设计模式面试题集
总题数: 36道 | 重点领域: 设计原则、创建型、结构型、行为型 | 难度分布: 中高级
本文档整理了 Java 设计模式的完整36道面试题目,涵盖设计原则、各种设计模式的应用场景和实现原理。
面试题目列表
1. 什么是设计模式?请简述其作用。
答案:
设计模式是软件开发中经过验证的、可复用的解决方案。
定义:
- 是在特定场景下对常见问题的通用解决方案
- 不是代码,而是解决问题的思路和方法
- 是前人经验的总结和提炼
主要作用:
- 提高代码复用性:减少重复代码
- 增强可维护性:代码结构清晰,易于理解
- 提升系统扩展性:方便添加新功能
- 降低耦合度:模块间依赖减少
- 统一开发语言:团队沟通更高效
2. 23 种设计模式分为哪三大类?
答案:
一、创建型模式(5种) 关注对象的创建过程
- 单例模式(Singleton)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
- 建造者模式(Builder)
- 原型模式(Prototype)
二、结构型模式(7种) 关注类和对象的组合
- 适配器模式(Adapter)
- 桥接模式(Bridge)
- 组合模式(Composite)
- 装饰器模式(Decorator)
- 外观模式(Facade)
- 享元模式(Flyweight)
- 代理模式(Proxy)
三、行为型模式(11种) 关注对象间的通信和职责分配
- 观察者模式(Observer)
- 迭代器模式(Iterator)
- 模板方法模式(Template Method)
- 命令模式(Command)
- 状态模式(State)
- 策略模式(Strategy)
- 责任链模式(Chain of Responsibility)
- 中介者模式(Mediator)
- 访问者模式(Visitor)
- 备忘录模式(Memento)
- 解释器模式(Interpreter)
3. 请解释什么是单例模式,并给出一个使用场景
答案:
单例模式确保一个类只有一个实例,并提供全局访问点。
核心要素:
- 私有构造函数(防止外部实例化)
- 静态实例变量
- 公共静态获取方法
基本实现:
1// 饥饿式(线程安全)2public class Singleton {3 // 静态实例4 private static final Singleton instance = new Singleton();5 6 // 私有构造7 private Singleton() {}8 9 // 公共获取方法10 public static Singleton getInstance() {11 return instance;12 }13}常见使用场景:
- 数据库连接池
1public class ConnectionPool {2 private static ConnectionPool instance;3 private List<Connection> connections;4 5 private ConnectionPool() {6 connections = new ArrayList<>();7 // 初始化连接池8 }9 10 public static ConnectionPool getInstance() {11 if (instance == null) {12 instance = new ConnectionPool();13 }14 return instance;15 }16}- 配置管理器
1public class ConfigManager {2 private static ConfigManager instance;3 private Properties config;4 5 private ConfigManager() {6 config = new Properties();7 // 加载配置文件8 }9 10 public static ConfigManager getInstance() {11 if (instance == null) {12 instance = new ConfigManager();13 }14 return instance;15 }16}- 日志工具
1public class Logger {2 private static Logger instance;3 4 private Logger() {}5 6 public static Logger getInstance() {7 if (instance == null) {8 instance = new Logger();9 }10 return instance;11 }12 13 public void log(String message) {14 System.out.println(message);15 }16}4. 单例模式有哪几种实现?如何保证线程安全?
答案:
1. 饥饿式(线程安全)
1public class Singleton {2 // 类加载时创建实例3 private static final Singleton instance = new Singleton();4 5 private Singleton() {}6 7 public static Singleton getInstance() {8 return instance;9 }10}1112// 优点:简单,线程安全13// 缺点:不能延迟加载2. 懒汉式(非线程安全)
1public class Singleton {2 private static Singleton instance;3 4 private Singleton() {}5 6 public static Singleton getInstance() {7 if (instance == null) {8 instance = new Singleton();9 }10 return instance;11 }12}1314// 优点:延迟加载15// 缺点:非线程安全3. 同步方法(线程安全,性能低)
1public class Singleton {2 private static Singleton instance;3 4 private Singleton() {}5 6 public static synchronized Singleton getInstance() {7 if (instance == null) {8 instance = new Singleton();9 }10 return instance;11 }12}1314// 优点:线程安全15// 缺点:每次调用都同步,性能低4. 双重检查锁(DCL,推荐)
1public class Singleton {2 // volatile保证可见性3 private static volatile Singleton instance;4 5 private Singleton() {}6 7 public static Singleton getInstance() {8 if (instance == null) { // 第一次检查9 synchronized (Singleton.class) {10 if (instance == null) { // 第二次检查11 instance = new Singleton();12 }13 }14 }15 return instance;16 }17}1819// 优点:线程安全,延迟加载,性能好20// 注意:必须使用volatile5. 静态内部类(推荐)
1public class Singleton {2 private Singleton() {}3 4 // 静态内部类5 private static class SingletonHolder {6 private static final Singleton INSTANCE = new Singleton();7 }8 9 public static Singleton getInstance() {10 return SingletonHolder.INSTANCE;11 }12}1314// 优点:线程安全,延迟加载,简洁15// 原理:类加载机制保证线程安全6. 枚举单例(最佳实践)
1public enum Singleton {2 INSTANCE;3 4 public void doSomething() {5 System.out.println("Singleton");6 }7}89// 使用10Singleton.INSTANCE.doSomething();1112// 优点:13// 1. 线程安全14// 2. 防止反射攻击15// 3. 防止反序列化创建新对象16// 4. 代码简洁线程安全对比:
| 实现方式 | 线程安全 | 延迟加载 | 性能 | 推荐度 |
|---|---|---|---|---|
| 饥饿式 | ✓ | × | 高 | ★★★ |
| 懒汉式 | × | ✓ | 高 | × |
| 同步方法 | ✓ | ✓ | 低 | ★ |
| 双重检查 | ✓ | ✓ | 高 | ★★★★ |
| 静态内部类 | ✓ | ✓ | 高 | ★★★★★ |
| 枚举 | ✓ | × | 高 | ★★★★★ |
5. 工厂模式和抽象工厂模式有什么区别?
答案:
工厂方法模式: 创建一种产品,不同工厂生产不同的实现。
1// 产品接口2interface Product {3 void use();4}56// 具体产品A7class ProductA implements Product {8 public void use() {9 System.out.println("Using Product A");10 }11}1213// 具体产品B14class ProductB implements Product {15 public void use() {16 System.out.println("Using Product B");17 }18}1920// 抽象工厂21abstract class Factory {22 abstract Product createProduct();23}2425// 具体工厂A26class FactoryA extends Factory {27 Product createProduct() {28 return new ProductA();29 }30}3132// 具体工厂B33class FactoryB extends Factory {34 Product createProduct() {35 return new ProductB();36 }37}抽象工厂模式: 创建一系列相关产品,一个工厂生产多种产品。
1// 产品族系1:按钮2interface Button {3 void click();4}56class WindowsButton implements Button {7 public void click() {8 System.out.println("Windows Button");9 }10}1112class MacButton implements Button {13 public void click() {14 System.out.println("Mac Button");15 }16}1718// 产品族系2:文本框19interface TextBox {20 void input();21}2223class WindowsTextBox implements TextBox {24 public void input() {25 System.out.println("Windows TextBox");26 }27}2829class MacTextBox implements TextBox {30 public void input() {31 System.out.println("Mac TextBox");32 }33}3435// 抽象工厂36interface GUIFactory {37 Button createButton();38 TextBox createTextBox();39}4041// Windows工厂42class WindowsFactory implements GUIFactory {43 public Button createButton() {44 return new WindowsButton();45 }46 public TextBox createTextBox() {47 return new WindowsTextBox();48 }49}5051// Mac工厂52class MacFactory implements GUIFactory {53 public Button createButton() {54 return new MacButton();55 }56 public TextBox createTextBox() {57 return new MacTextBox();58 }59}主要区别:
| 特性 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|
| 产品数量 | 一种产品 | 多种相关产品 |
| 工厂层次 | 一层 | 两层(抽象+具体) |
| 复杂度 | 简单 | 复杂 |
| 使用场景 | 单一产品的创建 | 产品族的创建 |
| 示例 | 创建不同数据库连接 | 创建不同UI风格组件 |
6. 请描述简单工厂模式的工作原理。
答案:
简单工厂模式通过一个工厂类根据参数创建不同的产品实例。
基本实现:
1// 产品接口2interface Shape {3 void draw();4}56// 具体产品7class Circle implements Shape {8 public void draw() {9 System.out.println("Drawing Circle");10 }11}1213class Rectangle implements Shape {14 public void draw() {15 System.out.println("Drawing Rectangle");16 }17}1819class Triangle implements Shape {20 public void draw() {21 System.out.println("Drawing Triangle");22 }23}2425// 简单工厂26class ShapeFactory {27 // 根据类型创建对象28 public static Shape createShape(String type) {29 if (type == null) {30 return null;31 }32 if (type.equalsIgnoreCase("CIRCLE")) {33 return new Circle();34 } else if (type.equalsIgnoreCase("RECTANGLE")) {35 return new Rectangle();36 } else if (type.equalsIgnoreCase("TRIANGLE")) {37 return new Triangle();38 }39 return null;40 }41}4243// 使用44public class Demo {45 public static void main(String[] args) {46 Shape circle = ShapeFactory.createShape("CIRCLE");47 circle.draw();48 49 Shape rectangle = ShapeFactory.createShape("RECTANGLE");50 rectangle.draw();51 }52}优缺点:
优点:
- 封装对象创建逻辑
- 客户端不需知道具体类名
- 代码简单,易于理解
缺点:
- 违反开闭原则(添加新产品需修改工厂类)
- 工厂类职责过重
7. 什么是建造者模式?一般用在什么场景?
答案:
建造者模式将复杂对象的构建过程与表示分离,逐步构建对象。
实现示例:
1// 产品类2class Computer {3 private String cpu;4 private String ram;5 private String storage;6 private String gpu;7 8 // 私有构造函数9 private Computer(Builder builder) {10 this.cpu = builder.cpu;11 this.ram = builder.ram;12 this.storage = builder.storage;13 this.gpu = builder.gpu;14 }15 16 // 静态内部建造者类17 public static class Builder {18 private String cpu;19 private String ram;20 private String storage;21 private String gpu;22 23 public Builder cpu(String cpu) {24 this.cpu = cpu;25 return this;26 }27 28 public Builder ram(String ram) {29 this.ram = ram;30 return this;31 }32 33 public Builder storage(String storage) {34 this.storage = storage;35 return this;36 }37 38 public Builder gpu(String gpu) {39 this.gpu = gpu;40 return this;41 }42 43 public Computer build() {44 return new Computer(this);45 }46 }47 48 @Override49 public String toString() {50 return "Computer{cpu='" + cpu + "', ram='" + ram + 51 "', storage='" + storage + "', gpu='" + gpu + "'}";52 }53}5455// 使用56public class Demo {57 public static void main(String[] args) {58 Computer computer = new Computer.Builder()59 .cpu("Intel i7")60 .ram("16GB")61 .storage("512GB SSD")62 .gpu("NVIDIA RTX 3060")63 .build();64 65 System.out.println(computer);66 }67}使用场景:
- 对象有很多参数:避免构造函数参数过多
- 参数有可选项:灵活配置对象
- 需要不可变对象:Builder构建后对象不可变
实际应用:
- StringBuilder/StringBuffer
- Lombok的@Builder注解
- OkHttp的Request.Builder
- Retrofit的配置
8. 什么是原型模式?一般用在什么场景?
答案:
原型模式通过复制现有对象来创建新对象,而不是通过new。
实现方式:
1. 浅克隆:
1class Sheep implements Cloneable {2 private String name;3 private int age;4 5 public Sheep(String name, int age) {6 this.name = name;7 this.age = age;8 }9 10 @Override11 protected Object clone() throws CloneNotSupportedException {12 return super.clone();13 }14}1516// 使用17Sheep original = new Sheep("Dolly", 3);18Sheep cloned = (Sheep) original.clone();2. 深克隆:
1class Address implements Cloneable {2 private String city;3 4 public Address(String city) {5 this.city = city;6 }7 8 @Override9 protected Object clone() throws CloneNotSupportedException {10 return super.clone();11 }12}1314class Person implements Cloneable {15 private String name;16 private Address address;17 18 public Person(String name, Address address) {19 this.name = name;20 this.address = address;21 }22 23 @Override24 protected Object clone() throws CloneNotSupportedException {25 Person cloned = (Person) super.clone();26 // 深克隆:复制引用对象27 cloned.address = (Address) address.clone();28 return cloned;29 }30}使用场景:
- 对象创建成本高:复制比新建快
- 需要保存对象状态:备份、撤销功能
- 避免复杂的初始化:直接复制已初始化对象
实际应用:
1// Spring中Bean的prototype作用域2@Scope("prototype")3public class MyBean {4 // ...5}67// Java集合的clone8ArrayList<String> list1 = new ArrayList<>();9ArrayList<String> list2 = (ArrayList<String>) list1.clone();9. 什么是适配器模式?一般用在什么场景?
答案:
适配器模式将一个类的接口转换成客户端期望的另一个接口。
类适配器(继承):
1// 目标接口2interface MediaPlayer {3 void play(String audioType, String fileName);4}56// 被适配的类7class VlcPlayer {8 public void playVlc(String fileName) {9 System.out.println("Playing vlc file: " + fileName);10 }11}1213class Mp4Player {14 public void playMp4(String fileName) {15 System.out.println("Playing mp4 file: " + fileName);16 }17}1819// 适配器20class MediaAdapter implements MediaPlayer {21 private VlcPlayer vlcPlayer;22 private Mp4Player mp4Player;23 24 public MediaAdapter(String audioType) {25 if (audioType.equalsIgnoreCase("vlc")) {26 vlcPlayer = new VlcPlayer();27 } else if (audioType.equalsIgnoreCase("mp4")) {28 mp4Player = new Mp4Player();29 }30 }31 32 @Override33 public void play(String audioType, String fileName) {34 if (audioType.equalsIgnoreCase("vlc")) {35 vlcPlayer.playVlc(fileName);36 } else if (audioType.equalsIgnoreCase("mp4")) {37 mp4Player.playMp4(fileName);38 }39 }40}4142// 使用43class AudioPlayer implements MediaPlayer {44 private MediaAdapter mediaAdapter;45 46 @Override47 public void play(String audioType, String fileName) {48 if (audioType.equalsIgnoreCase("mp3")) {49 System.out.println("Playing mp3 file: " + fileName);50 } else if (audioType.equalsIgnoreCase("vlc") || 51 audioType.equalsIgnoreCase("mp4")) {52 mediaAdapter = new MediaAdapter(audioType);53 mediaAdapter.play(audioType, fileName);54 } else {55 System.out.println("Invalid media type");56 }57 }58}使用场景:
- 系统集成:旧系统与新系统接口不兼容
- 第三方库适配:封装第三方接口
- 接口转换:不同接口间的转换
实际应用:
1// Java IO中的适配器2InputStreamReader isr = new InputStreamReader(new FileInputStream("file.txt"));34// Arrays.asList()5List<String> list = Arrays.asList("a", "b", "c");67// Spring MVC中的HandlerAdapter10. 什么是桥接模式?一般用在什么场景?
答案:
桥接模式将抽象部分与实现部分分离,使它们可以独立变化。
实现示例:
1// 实现部分:颜色2interface Color {3 void applyColor();4}56class RedColor implements Color {7 public void applyColor() {8 System.out.println("Red color");9 }10}1112class BlueColor implements Color {13 public void applyColor() {14 System.out.println("Blue color");15 }16}1718// 抽象部分:形状19abstract class Shape {20 protected Color color; // 桥接21 22 public Shape(Color color) {23 this.color = color;24 }25 26 abstract void draw();27}2829class Circle extends Shape {30 public Circle(Color color) {31 super(color);32 }33 34 @Override35 void draw() {36 System.out.print("Circle with ");37 color.applyColor();38 }39}4041class Rectangle extends Shape {42 public Rectangle(Color color) {43 super(color);44 }45 46 @Override47 void draw() {48 System.out.print("Rectangle with ");49 color.applyColor();50 }51}5253// 使用54public class Demo {55 public static void main(String[] args) {56 Shape redCircle = new Circle(new RedColor());57 redCircle.draw();58 59 Shape blueRectangle = new Rectangle(new BlueColor());60 blueRectangle.draw();61 }62}优势:
- 抽象和实现分离,可独立扩展
- 避免类爆炸(不用为每种组合创建类)
- 符合开闭原则
使用场景:
- 多维度变化:形状+颜色、设备+系统
- 避免继承爆炸:用组合替代继承
- 跨平台开发:业务逻辑与平台实现分离
实际应用:
1// JDBC驱动2Connection conn = DriverManager.getConnection(url);34// 日志框架:SLF4J + Logback/Log4j5Logger logger = LoggerFactory.getLogger(MyClass.class);11. 什么是组合模式?一般用在什么场景?
答案:
组合模式将对象组合成树形结构,表示“部分-整体”层次结构。
实现示例:
1// 组件接口2abstract class Component {3 protected String name;4 5 public Component(String name) {6 this.name = name;7 }8 9 abstract void add(Component component);10 abstract void remove(Component component);11 abstract void display(int depth);12}1314// 叶子节点:文件15class File extends Component {16 public File(String name) {17 super(name);18 }19 20 @Override21 void add(Component component) {22 throw new UnsupportedOperationException();23 }24 25 @Override26 void remove(Component component) {27 throw new UnsupportedOperationException();28 }29 30 @Override31 void display(int depth) {32 System.out.println("-".repeat(depth) + name);33 }34}3536// 容器节点:文件夹37class Folder extends Component {38 private List<Component> children = new ArrayList<>();39 40 public Folder(String name) {41 super(name);42 }43 44 @Override45 void add(Component component) {46 children.add(component);47 }48 49 @Override50 void remove(Component component) {51 children.remove(component);52 }53 54 @Override55 void display(int depth) {56 System.out.println("-".repeat(depth) + name);57 for (Component component : children) {58 component.display(depth + 2);59 }60 }61}6263// 使用64public class Demo {65 public static void main(String[] args) {66 Folder root = new Folder("root");67 Folder folder1 = new Folder("folder1");68 Folder folder2 = new Folder("folder2");69 70 File file1 = new File("file1.txt");71 File file2 = new File("file2.txt");72 File file3 = new File("file3.txt");73 74 root.add(folder1);75 root.add(folder2);76 folder1.add(file1);77 folder1.add(file2);78 folder2.add(file3);79 80 root.display(0);81 }82}使用场景:
- 树形结构:文件系统、组织架构
- 部分-整体关系:菜单系统、UI组件
- 递归处理:需要递归遍历的场景
12. 什么是装饰器模式?一般用在什么场景?
答案:
装饰器模式动态地给对象添加额外的职责,不改变原有结构。
实现示例:
1// 组件接口2interface Coffee {3 double cost();4 String description();5}67// 具体组件8class SimpleCoffee implements Coffee {9 @Override10 public double cost() {11 return 10.0;12 }13 14 @Override15 public String description() {16 return "Simple Coffee";17 }18}1920// 抽象装饰器21abstract class CoffeeDecorator implements Coffee {22 protected Coffee coffee;23 24 public CoffeeDecorator(Coffee coffee) {25 this.coffee = coffee;26 }27 28 @Override29 public double cost() {30 return coffee.cost();31 }32 33 @Override34 public String description() {35 return coffee.description();36 }37}3839// 具体装饰器:牛奶40class MilkDecorator extends CoffeeDecorator {41 public MilkDecorator(Coffee coffee) {42 super(coffee);43 }44 45 @Override46 public double cost() {47 return super.cost() + 2.0;48 }49 50 @Override51 public String description() {52 return super.description() + ", Milk";53 }54}5556// 具体装饰器:糖57class SugarDecorator extends CoffeeDecorator {58 public SugarDecorator(Coffee coffee) {59 super(coffee);60 }61 62 @Override63 public double cost() {64 return super.cost() + 1.0;65 }66 67 @Override68 public String description() {69 return super.description() + ", Sugar";70 }71}7273// 使用74public class Demo {75 public static void main(String[] args) {76 Coffee coffee = new SimpleCoffee();77 System.out.println(coffee.description() + " $" + coffee.cost());78 79 coffee = new MilkDecorator(coffee);80 System.out.println(coffee.description() + " $" + coffee.cost());81 82 coffee = new SugarDecorator(coffee);83 System.out.println(coffee.description() + " $" + coffee.cost());84 }85}使用场景:
- 动态扩展功能:不修改原类添加功能
- 避免类爆炸:不用为每种组合创建子类
- 符合开闭原则:对扩展开放,对修改关闭
实际应用:
- Java IO:BufferedReader/BufferedWriter
- Collections.synchronizedList()
- Spring的BeanWrapper
13. 装饰器、适配器、代理、桥接这四种设计模式有什么区别?
答案:
| 模式 | 目的 | 特点 | 示例 |
|---|---|---|---|
| 装饰器 | 增强功能 | 不改变接口,动态添加职责 | BufferedReader |
| 适配器 | 接口转换 | 让不兼容的接口协同工作 | InputStreamReader |
| 代理 | 控制访问 | 为对象提供代理,控制访问 | Spring AOP |
| 桥接 | 分离抽象和实现 | 抽象和实现可独立变化 | JDBC Driver |
详细对比:
1. 装饰器模式
1// 目的:增强功能2Reader reader = new FileReader("file.txt");3reader = new BufferedReader(reader); // 添加缓冲功能2. 适配器模式
1// 目的:接口转换2InputStream is = new FileInputStream("file.txt");3Reader reader = new InputStreamReader(is); // 字节流转字符流3. 代理模式
1// 目的:控制访问2UserService proxy = (UserService) Proxy.newProxyInstance(3 UserService.class.getClassLoader(),4 new Class[]{UserService.class},5 new InvocationHandler() {6 public Object invoke(Object proxy, Method method, Object[] args) {7 // 前置处理8 Object result = method.invoke(target, args);9 // 后置处理10 return result;11 }12 }13);4. 桥接模式
1// 目的:分离抽象和实现2abstract class Shape {3 protected Color color; // 桥接4 abstract void draw();5}核心区别:
- 装饰器:同接口,增强功能
- 适配器:不同接口,转换接口
- 代理:同接口,控制访问
- 桥接:分离抽象,独立变化
14. 什么是外观模式?一般用在什么场景?
答案:
外观模式为复杂子系统提供一个统一的高层接口,简化调用。
实现示例:
1// 子系统1:CPU2class CPU {3 public void start() {4 System.out.println("CPU started");5 }6 7 public void shutdown() {8 System.out.println("CPU shutdown");9 }10}1112// 子系统2:内存13class Memory {14 public void load() {15 System.out.println("Memory loaded");16 }17}1819// 子系统3:硬盘20class HardDrive {21 public void read() {22 System.out.println("Hard drive reading");23 }24}2526// 外观类27class ComputerFacade {28 private CPU cpu;29 private Memory memory;30 private HardDrive hardDrive;31 32 public ComputerFacade() {33 this.cpu = new CPU();34 this.memory = new Memory();35 this.hardDrive = new HardDrive();36 }37 38 // 统一的启动接口39 public void start() {40 cpu.start();41 memory.load();42 hardDrive.read();43 System.out.println("Computer started");44 }45 46 // 统一的关闭接口47 public void shutdown() {48 cpu.shutdown();49 System.out.println("Computer shutdown");50 }51}5253// 使用54public class Demo {55 public static void main(String[] args) {56 ComputerFacade computer = new ComputerFacade();57 computer.start(); // 简单调用58 computer.shutdown();59 }60}使用场景:
- 简化复杂系统:为复杂子系统提供简单接口
- 分层架构:层与层之间的接口
- 降低耦合:客户端与子系统解耦
实际应用:
- SLF4J日志外观
- JDBC的DriverManager
- Spring的各种Template类
15. 什么是享元模式?一般用在什么场景?
答案:
享元模式通过共享技术有效支持大量细粒度对象,减少内存开销。
实现示例:
1// 享元接口2interface Shape {3 void draw(int x, int y);4}56// 具体享元类7class Circle implements Shape {8 private String color; // 内部状态(共享)9 10 public Circle(String color) {11 this.color = color;12 }13 14 @Override15 public void draw(int x, int y) { // 外部状态(不共享)16 System.out.println("Drawing " + color + " circle at (" + x + ", " + y + ")");17 }18}1920// 享元工厂21class ShapeFactory {22 private static final Map<String, Shape> circleMap = new HashMap<>();23 24 public static Shape getCircle(String color) {25 Circle circle = (Circle) circleMap.get(color);26 27 if (circle == null) {28 circle = new Circle(color);29 circleMap.put(color, circle);30 System.out.println("Creating " + color + " circle");31 }32 33 return circle;34 }35}3637// 使用38public class Demo {39 public static void main(String[] args) {40 String[] colors = {"Red", "Green", "Blue", "Red", "Green"};41 42 for (int i = 0; i < 5; i++) {43 Shape circle = ShapeFactory.getCircle(colors[i]);44 circle.draw(i * 10, i * 20);45 }46 47 // 只创建了3个对象,而不是5个48 }49}核心概念:
- 内部状态:可共享的不变部分(如颜色)
- 外部状态:不可共享的变化部分(如坐标)
使用场景:
- 大量相似对象:游戏中的子弹、树木
- 内存优化:减少对象创建数量
- 不变对象:对象状态不经常变化
实际应用:
1// String常量池2String s1 = "hello";3String s2 = "hello"; // 共享同一对象45// Integer缓存(-128到127)6Integer i1 = 100;7Integer i2 = 100; // 共享同一对象89// 数据库连接池16. 什么是代理模式?一般用在什么场景?
答案:
代理模式为对象提供一个代理,控制对该对象的访问。
静态代理:
1// 接口2interface UserService {3 void save(String user);4}56// 真实对象7class UserServiceImpl implements UserService {8 @Override9 public void save(String user) {10 System.out.println("Saving user: " + user);11 }12}1314// 代理类15class UserServiceProxy implements UserService {16 private UserService target;17 18 public UserServiceProxy(UserService target) {19 this.target = target;20 }21 22 @Override23 public void save(String user) {24 System.out.println("Before save - logging");25 target.save(user);26 System.out.println("After save - logging");27 }28}2930// 使用31UserService service = new UserServiceImpl();32UserService proxy = new UserServiceProxy(service);33proxy.save("John");JDK动态代理:
1UserService service = new UserServiceImpl();23UserService proxy = (UserService) Proxy.newProxyInstance(4 service.getClass().getClassLoader(),5 service.getClass().getInterfaces(),6 new InvocationHandler() {7 @Override8 public Object invoke(Object proxy, Method method, Object[] args) 9 throws Throwable {10 System.out.println("Before: " + method.getName());11 Object result = method.invoke(service, args);12 System.out.println("After: " + method.getName());13 return result;14 }15 }16);1718proxy.save("John");CGLIB代理:
1class UserService {2 public void save(String user) {3 System.out.println("Saving: " + user);4 }5}67Enhancer enhancer = new Enhancer();8enhancer.setSuperclass(UserService.class);9enhancer.setCallback(new MethodInterceptor() {10 @Override11 public Object intercept(Object obj, Method method, Object[] args, 12 MethodProxy proxy) throws Throwable {13 System.out.println("Before");14 Object result = proxy.invokeSuper(obj, args);15 System.out.println("After");16 return result;17 }18});1920UserService proxy = (UserService) enhancer.create();21proxy.save("John");使用场景:
- 远程代理:RPC、RMI
- 虚拟代理:延迟加载大对象
- 保护代理:权限控制
- 智能代理:日志、缓存、事务
实际应用:
- Spring AOP
- MyBatis Mapper接口
- Feign远程调用
17. 什么是观察者模式?一般用在什么场景?
答案:
观察者模式定义对象间的一对多依赖,当一个对象状态改变时,所有依赖它的对象都会收到通知。
实现示例:
1// 观察者接口2interface Observer {3 void update(String message);4}56// 具体观察者7class User implements Observer {8 private String name;9 10 public User(String name) {11 this.name = name;12 }13 14 @Override15 public void update(String message) {16 System.out.println(name + " received: " + message);17 }18}1920// 主题(被观察者)21class Subject {22 private List<Observer> observers = new ArrayList<>();23 24 // 添加观察者25 public void attach(Observer observer) {26 observers.add(observer);27 }28 29 // 移除观察者30 public void detach(Observer observer) {31 observers.remove(observer);32 }33 34 // 通知所有观察者35 public void notifyObservers(String message) {36 for (Observer observer : observers) {37 observer.update(message);38 }39 }40}4142// 具体主题43class NewsAgency extends Subject {44 public void publishNews(String news) {45 System.out.println("Publishing news: " + news);46 notifyObservers(news);47 }48}4950// 使用51public class Demo {52 public static void main(String[] args) {53 NewsAgency agency = new NewsAgency();54 55 User user1 = new User("Alice");56 User user2 = new User("Bob");57 58 agency.attach(user1);59 agency.attach(user2);60 61 agency.publishNews("Breaking News!");62 }63}使用场景:
- 事件处理:GUI事件监听
- 消息通知:订阅发布系统
- 数据同步:模型-视图同步
实际应用:
1// Java内置支持2java.util.Observable3java.util.Observer45// Spring事件6ApplicationEventPublisher78// MQ消息队列9Kafka, RabbitMQ18. 什么是迭代器模式?一般用在什么场景?
答案:
迭代器模式提供一种方法顺序访问聚合对象中的元素,而不暴露其内部表示。
实现示例:
1// 迭代器接口2interface Iterator<T> {3 boolean hasNext();4 T next();5}67// 聚合接口8interface Container<T> {9 Iterator<T> getIterator();10}1112// 具体聚合13class BookCollection implements Container<String> {14 private String[] books;15 private int index = 0;16 17 public BookCollection(int size) {18 books = new String[size];19 }20 21 public void addBook(String book) {22 if (index < books.length) {23 books[index++] = book;24 }25 }26 27 @Override28 public Iterator<String> getIterator() {29 return new BookIterator();30 }31 32 // 内部迭代器类33 private class BookIterator implements Iterator<String> {34 private int currentIndex = 0;35 36 @Override37 public boolean hasNext() {38 return currentIndex < index;39 }40 41 @Override42 public String next() {43 if (hasNext()) {44 return books[currentIndex++];45 }46 return null;47 }48 }49}5051// 使用52public class Demo {53 public static void main(String[] args) {54 BookCollection books = new BookCollection(3);55 books.addBook("Design Patterns");56 books.addBook("Clean Code");57 books.addBook("Refactoring");58 59 Iterator<String> iterator = books.getIterator();60 while (iterator.hasNext()) {61 System.out.println(iterator.next());62 }63 }64}使用场景:
- 聚合遍历:统一遍历接口
- 隐藏内部结构:不暴露实现细节
- 多种遍历方式:支持不同遍历策略
实际应用:
1// Java集合框架2List<String> list = new ArrayList<>();3Iterator<String> iterator = list.iterator();45// for-each语法糖6for (String item : list) {7 // 内部使用迭代器8}19. 什么是模板方法模式?一般用在什么场景?
答案:
模板方法模式定义算法骨架,将某些步骤延迟到子类实现。
实现示例:
1// 抽象类2abstract class DataProcessor {3 // 模板方法:定义算法骨架4 public final void process() {5 readData();6 processData();7 saveData();8 }9 10 // 具体方法11 private void readData() {12 System.out.println("Reading data...");13 }14 15 // 抽象方法:由子类实现16 protected abstract void processData();17 18 // 钩子方法:可选重写19 protected void saveData() {20 System.out.println("Saving data...");21 }22}2324// 具体子类125class CSVDataProcessor extends DataProcessor {26 @Override27 protected void processData() {28 System.out.println("Processing CSV data");29 }30}3132// 具体子类233class JSONDataProcessor extends DataProcessor {34 @Override35 protected void processData() {36 System.out.println("Processing JSON data");37 }38 39 @Override40 protected void saveData() {41 System.out.println("Saving JSON to database");42 }43}4445// 使用46public class Demo {47 public static void main(String[] args) {48 DataProcessor csv = new CSVDataProcessor();49 csv.process();50 51 System.out.println();52 53 DataProcessor json = new JSONDataProcessor();54 json.process();55 }56}使用场景:
- 算法骨架固定:步骤固定,细节变化
- 代码复用:公共代码提取到父类
- 控制扩展:只允许特定步骤扩展
实际应用:
1// Servlet的service方法2public abstract class HttpServlet {3 public void service(HttpServletRequest req, HttpServletResponse resp) {4 String method = req.getMethod();5 if (method.equals("GET")) {6 doGet(req, resp);7 } else if (method.equals("POST")) {8 doPost(req, resp);9 }10 }11 12 protected abstract void doGet(HttpServletRequest req, HttpServletResponse resp);13 protected abstract void doPost(HttpServletRequest req, HttpServletResponse resp);14}1516// Spring的JdbcTemplate17// MyBatis的BaseExecutor20. 什么是命令模式?一般用在什么场景?
答案:
命令模式将请求封装为对象,从而可以参数化、队列化、记录请求。
实现示例:
1// 命令接口2interface Command {3 void execute();4 void undo();5}67// 接收者:灯8class Light {9 public void on() {10 System.out.println("Light is ON");11 }12 13 public void off() {14 System.out.println("Light is OFF");15 }16}1718// 具体命令:开灯19class LightOnCommand implements Command {20 private Light light;21 22 public LightOnCommand(Light light) {23 this.light = light;24 }25 26 @Override27 public void execute() {28 light.on();29 }30 31 @Override32 public void undo() {33 light.off();34 }35}3637// 具体命令:关灯38class LightOffCommand implements Command {39 private Light light;40 41 public LightOffCommand(Light light) {42 this.light = light;43 }44 45 @Override46 public void execute() {47 light.off();48 }49 50 @Override51 public void undo() {52 light.on();53 }54}5556// 调用者:遥控器57class RemoteControl {58 private Command command;59 60 public void setCommand(Command command) {61 this.command = command;62 }63 64 public void pressButton() {65 command.execute();66 }67 68 public void pressUndo() {69 command.undo();70 }71}7273// 使用74public class Demo {75 public static void main(String[] args) {76 Light light = new Light();77 Command lightOn = new LightOnCommand(light);78 Command lightOff = new LightOffCommand(light);79 80 RemoteControl remote = new RemoteControl();81 82 remote.setCommand(lightOn);83 remote.pressButton(); // 开灯84 remote.pressUndo(); // 撤销,关灯85 86 remote.setCommand(lightOff);87 remote.pressButton(); // 关灯88 }89}使用场景:
- 撤销/重做:编辑器操作
- 队列请求:任务队列
- 记录日志:操作日志
- 宏命令:组合多个命令
实际应用:
1// Runnable接口2Runnable task = () -> System.out.println("Task");3new Thread(task).start();45// Spring的JdbcTemplate回调6// Struts2的Action21. 什么是状态模式?一般用在什么场景?
答案:
状态模式允许对象在内部状态改变时改变其行为。
实现示例:
1// 状态接口2interface State {3 void handle(Context context);4}56// 具体状态:开始状态7class StartState implements State {8 @Override9 public void handle(Context context) {10 System.out.println("Starting...");11 context.setState(new RunningState());12 }13}1415// 具体状态:运行状态16class RunningState implements State {17 @Override18 public void handle(Context context) {19 System.out.println("Running...");20 context.setState(new StopState());21 }22}2324// 具体状态:停止状态25class StopState implements State {26 @Override27 public void handle(Context context) {28 System.out.println("Stopped.");29 context.setState(null);30 }31}3233// 上下文类34class Context {35 private State state;36 37 public Context() {38 this.state = new StartState();39 }40 41 public void setState(State state) {42 this.state = state;43 }44 45 public void request() {46 if (state != null) {47 state.handle(this);48 }49 }50}5152// 使用53public class Demo {54 public static void main(String[] args) {55 Context context = new Context();56 context.request(); // Starting57 context.request(); // Running58 context.request(); // Stopped59 }60}使用场景:
- 状态机:工作流、订单状态
- 条件分支复杂:替代大量if-else
- 状态转换:状态间有明确转换规则
实际应用:
- TCP连接状态
- 订单状态流转
- 线程状态
22. 什么是策略模式?一般用在什么场景?
答案:
策略模式定义一系列算法,将每个算法封装起来,使它们可以互相替换。
实现示例:
1// 策略接口2interface PaymentStrategy {3 void pay(int amount);4}56// 具体策略:支付宝7class AlipayStrategy implements PaymentStrategy {8 @Override9 public void pay(int amount) {10 System.out.println("Paid " + amount + " using Alipay");11 }12}1314// 具体策略:微信15class WeChatStrategy implements PaymentStrategy {16 @Override17 public void pay(int amount) {18 System.out.println("Paid " + amount + " using WeChat");19 }20}2122// 具体策略:银行卡23class BankCardStrategy implements PaymentStrategy {24 @Override25 public void pay(int amount) {26 System.out.println("Paid " + amount + " using Bank Card");27 }28}2930// 上下文类31class ShoppingCart {32 private PaymentStrategy paymentStrategy;33 34 public void setPaymentStrategy(PaymentStrategy strategy) {35 this.paymentStrategy = strategy;36 }37 38 public void checkout(int amount) {39 paymentStrategy.pay(amount);40 }41}4243// 使用44public class Demo {45 public static void main(String[] args) {46 ShoppingCart cart = new ShoppingCart();47 48 // 使用支付宝49 cart.setPaymentStrategy(new AlipayStrategy());50 cart.checkout(100);51 52 // 切换为微信53 cart.setPaymentStrategy(new WeChatStrategy());54 cart.checkout(200);55 }56}使用场景:
- 多种算法可选:支付方式、排序算法
- 避免if-else:替代复杂条件分支
- 算法独立变化:算法可独立扩展
实际应用:
1// Comparator2Collections.sort(list, new Comparator<String>() {3 public int compare(String s1, String s2) {4 return s1.compareTo(s2);5 }6});78// ThreadPoolExecutor的拒绝策略9RejectedExecutionHandler23. 什么是责任链模式?一般用在什么场景?
答案:
责任链模式将请求沿着处理者链传递,直到有处理者处理它。
实现示例:
1// 抽象处理者2abstract class Handler {3 protected Handler nextHandler;4 5 public void setNext(Handler handler) {6 this.nextHandler = handler;7 }8 9 public abstract void handleRequest(int level);10}1112// 具体处理者:经理13class Manager extends Handler {14 @Override15 public void handleRequest(int level) {16 if (level <= 1) {17 System.out.println("Manager handled request level " + level);18 } else if (nextHandler != null) {19 nextHandler.handleRequest(level);20 }21 }22}2324// 具体处理者:总监25class Director extends Handler {26 @Override27 public void handleRequest(int level) {28 if (level <= 2) {29 System.out.println("Director handled request level " + level);30 } else if (nextHandler != null) {31 nextHandler.handleRequest(level);32 }33 }34}3536// 具体处理者:CEO37class CEO extends Handler {38 @Override39 public void handleRequest(int level) {40 System.out.println("CEO handled request level " + level);41 }42}4344// 使用45public class Demo {46 public static void main(String[] args) {47 Handler manager = new Manager();48 Handler director = new Director();49 Handler ceo = new CEO();50 51 // 构建责任链52 manager.setNext(director);53 director.setNext(ceo);54 55 // 发起请求56 manager.handleRequest(1); // Manager处理57 manager.handleRequest(2); // Director处理58 manager.handleRequest(3); // CEO处理59 }60}使用场景:
- 审批流程:请假、报销审批
- 过滤器链:Servlet Filter
- 异常处理:多级异常捕获
实际应用:
1// Servlet Filter链2FilterChain34// Netty的ChannelPipeline5pipeline.addLast(new Handler1());6pipeline.addLast(new Handler2());78// Spring Security过滤器链24. 什么是中介者模式?一般用在什么场景?
答案:
中介者模式用一个中介对象封装一系列对象的交互,降低对象间的耦合。
实现示例:
1// 中介者接口2interface ChatMediator {3 void sendMessage(String message, User user);4 void addUser(User user);5}67// 具体中介者8class ChatRoom implements ChatMediator {9 private List<User> users = new ArrayList<>();10 11 @Override12 public void addUser(User user) {13 users.add(user);14 }15 16 @Override17 public void sendMessage(String message, User sender) {18 for (User user : users) {19 // 不发给发送者自己20 if (user != sender) {21 user.receive(message);22 }23 }24 }25}2627// 同事类28class User {29 private String name;30 private ChatMediator mediator;31 32 public User(String name, ChatMediator mediator) {33 this.name = name;34 this.mediator = mediator;35 }36 37 public void send(String message) {38 System.out.println(name + " sends: " + message);39 mediator.sendMessage(message, this);40 }41 42 public void receive(String message) {43 System.out.println(name + " received: " + message);44 }45}4647// 使用48public class Demo {49 public static void main(String[] args) {50 ChatMediator chatRoom = new ChatRoom();51 52 User alice = new User("Alice", chatRoom);53 User bob = new User("Bob", chatRoom);54 User charlie = new User("Charlie", chatRoom);55 56 chatRoom.addUser(alice);57 chatRoom.addUser(bob);58 chatRoom.addUser(charlie);59 60 alice.send("Hello everyone!");61 }62}使用场景:
- 复杂交互:多对象互相通信
- 降低耦合:对象间不直接引用
- 集中控制:统一管理交互逻辑
实际应用:
- MVC中的Controller
- 聊天室服务器
- 机场控制塔
25. 什么是访问者模式?一般用在什么场景?
答案:
访问者模式将数据结构与操作分离,在不改变数据结构的前提下添加新操作。
实现示例:
1// 访问者接口2interface Visitor {3 void visit(Book book);4 void visit(Fruit fruit);5}67// 元素接口8interface Element {9 void accept(Visitor visitor);10}1112// 具体元素:书13class Book implements Element {14 private String name;15 private double price;16 17 public Book(String name, double price) {18 this.name = name;19 this.price = price;20 }21 22 public String getName() { return name; }23 public double getPrice() { return price; }24 25 @Override26 public void accept(Visitor visitor) {27 visitor.visit(this);28 }29}3031// 具体元素:水果32class Fruit implements Element {33 private String name;34 private double weight;35 private double pricePerKg;36 37 public Fruit(String name, double weight, double pricePerKg) {38 this.name = name;39 this.weight = weight;40 this.pricePerKg = pricePerKg;41 }42 43 public String getName() { return name; }44 public double getWeight() { return weight; }45 public double getPricePerKg() { return pricePerKg; }46 47 @Override48 public void accept(Visitor visitor) {49 visitor.visit(this);50 }51}5253// 具体访问者:计算价格54class PriceCalculator implements Visitor {55 private double totalPrice = 0;56 57 @Override58 public void visit(Book book) {59 totalPrice += book.getPrice();60 System.out.println("Book: " + book.getName() + " - $" + book.getPrice());61 }62 63 @Override64 public void visit(Fruit fruit) {65 double price = fruit.getWeight() * fruit.getPricePerKg();66 totalPrice += price;67 System.out.println("Fruit: " + fruit.getName() + " - $" + price);68 }69 70 public double getTotalPrice() {71 return totalPrice;72 }73}7475// 使用76public class Demo {77 public static void main(String[] args) {78 List<Element> items = new ArrayList<>();79 items.add(new Book("Design Patterns", 50));80 items.add(new Fruit("Apple", 2, 5));81 items.add(new Book("Clean Code", 45));82 83 PriceCalculator calculator = new PriceCalculator();84 for (Element item : items) {85 item.accept(calculator);86 }87 88 System.out.println("Total: $" + calculator.getTotalPrice());89 }90}使用场景:
- 数据结构稳定:结构不变,操作多变
- 多种操作:需要对元素执行多种不同操作
- 分离关注点:数据与算法分离
实际应用:
- 编译器的AST遍历
- XML文档处理
- 文件系统操作
26. 什么是备忘录模式?一般用在什么场景?
答案:
备忘录模式在不破坏封装的前提下,捕获对象的内部状态并保存,以便后续恢复。
实现示例:
1// 备忘录类2class Memento {3 private String state;4 5 public Memento(String state) {6 this.state = state;7 }8 9 public String getState() {10 return state;11 }12}1314// 原发器15class Editor {16 private String content;17 18 public void setContent(String content) {19 this.content = content;20 }21 22 public String getContent() {23 return content;24 }25 26 // 保存状态27 public Memento save() {28 return new Memento(content);29 }30 31 // 恢复状态32 public void restore(Memento memento) {33 this.content = memento.getState();34 }35}3637// 管理者38class History {39 private Stack<Memento> mementos = new Stack<>();40 41 public void push(Memento memento) {42 mementos.push(memento);43 }44 45 public Memento pop() {46 return mementos.pop();47 }48}4950// 使用51Editor editor = new Editor();52History history = new History();5354editor.setContent("Version 1");55history.push(editor.save());5657editor.setContent("Version 2");58history.push(editor.save());5960editor.setContent("Version 3");6162// 撤销63editor.restore(history.pop());64System.out.println(editor.getContent()); // Version 2使用场景:
- 撤销/重做功能
- 事务回滚
- 游戏存档
27. 什么是单一职责原则?
答案:
单一职责原则(SRP):一个类应该只有一个引起它变化的原因。
核心思想:
- 一个类只负责一项职责
- 降低类的复杂度
- 提高可读性和可维护性
示例:
1// 违反SRP:一个类承担多个职责2class User {3 private String name;4 5 // 职责1:用户数据管理6 public void setName(String name) {7 this.name = name;8 }9 10 // 职责2:数据库操作11 public void save() {12 // 保存到数据库13 }14 15 // 职责3:邮件发送16 public void sendEmail() {17 // 发送邮件18 }19}2021// 符合SRP:职责分离22class User {23 private String name;24 public void setName(String name) { this.name = name; }25}2627class UserRepository {28 public void save(User user) { /* 保存 */ }29}3031class EmailService {32 public void sendEmail(User user) { /* 发送 */ }33}28. 什么是开闭原则?
答案:
开闭原则(OCP):软件实体应该对扩展开放,对修改关闭。
核心思想:
- 通过扩展实现变化,而不是修改现有代码
- 使用抽象和多态
- 提高系统稳定性
示例:
1// 违反OCP:需要修改代码添加新功能2class Calculator {3 public double calculate(String type, double a, double b) {4 if (type.equals("add")) {5 return a + b;6 } else if (type.equals("subtract")) {7 return a - b;8 }9 // 添加新运算需要修改此方法10 return 0;11 }12}1314// 符合OCP:通过扩展添加新功能15interface Operation {16 double calculate(double a, double b);17}1819class Add implements Operation {20 public double calculate(double a, double b) {21 return a + b;22 }23}2425class Subtract implements Operation {26 public double calculate(double a, double b) {27 return a - b;28 }29}3031// 添加新运算只需新增类,无需修改现有代码32class Multiply implements Operation {33 public double calculate(double a, double b) {34 return a * b;35 }36}29. 什么是里氏替换原则?
答案:
里氏替换原则(LSP):子类对象能够替换父类对象,程序行为不变。
核心思想:
- 子类必须完全实现父类的方法
- 子类可以有自己的特性
- 覆盖父类方法时,输入参数可以放宽,输出结果应该收紧
示例:
1// 违反LSP2class Rectangle {3 protected int width;4 protected int height;5 6 public void setWidth(int width) { this.width = width; }7 public void setHeight(int height) { this.height = height; }8 public int getArea() { return width * height; }9}1011class Square extends Rectangle {12 @Override13 public void setWidth(int width) {14 this.width = width;15 this.height = width; // 破坏了父类行为16 }17}1819// 符合LSP:使用组合而非继承20interface Shape {21 int getArea();22}2324class Rectangle implements Shape {25 private int width;26 private int height;27 28 public int getArea() { return width * height; }29}3031class Square implements Shape {32 private int side;33 34 public int getArea() { return side * side; }35}30. 什么是接口隔离原则?
答案:
接口隔离原则(ISP):客户端不应该依赖它不需要的接口。
核心思想:
- 接口应该小而专一
- 避免臃肿的接口
- 一个类对另一个类的依赖应该建立在最小接口上
示例:
1// 违反ISP:臃肿的接口2interface Worker {3 void work();4 void eat();5 void sleep();6}78class Human implements Worker {9 public void work() { /* 工作 */ }10 public void eat() { /* 吃饭 */ }11 public void sleep() { /* 睡觉 */ }12}1314class Robot implements Worker {15 public void work() { /* 工作 */ }16 public void eat() { /* 机器人不需要 */ }17 public void sleep() { /* 机器人不需要 */ }18}1920// 符合ISP:接口分离21interface Workable {22 void work();23}2425interface Eatable {26 void eat();27}2829interface Sleepable {30 void sleep();31}3233class Human implements Workable, Eatable, Sleepable {34 public void work() { /* 工作 */ }35 public void eat() { /* 吃饭 */ }36 public void sleep() { /* 睡觉 */ }37}3839class Robot implements Workable {40 public void work() { /* 工作 */ }41}31. 什么是依赖倒置原则?
答案:
依赖倒置原则(DIP):高层模块不应该依赖低层模块,两者都应该依赖抽象。
核心思想:
- 面向接口编程,不面向实现编程
- 抽象不应该依赖细节,细节应该依赖抽象
- 降低耦合度
示例:
1// 违反DIP:高层依赖低层具体实现2class MySQLDatabase {3 public void connect() {4 System.out.println("MySQL connected");5 }6}78class UserService {9 private MySQLDatabase database = new MySQLDatabase();10 11 public void getUser() {12 database.connect();13 // 获取用户14 }15}1617// 符合DIP:依赖抽象18interface Database {19 void connect();20}2122class MySQLDatabase implements Database {23 public void connect() {24 System.out.println("MySQL connected");25 }26}2728class MongoDatabase implements Database {29 public void connect() {30 System.out.println("MongoDB connected");31 }32}3334class UserService {35 private Database database;36 37 // 依赖注入38 public UserService(Database database) {39 this.database = database;40 }41 42 public void getUser() {43 database.connect();44 // 获取用户45 }46}32. 什么是迪米特法则?
答案:
迪米特法则(LoD):一个对象应该对其他对象有最少的了解,只与直接的朋友通信。
核心思想:
- 降低类之间的耦合
- 类只与直接朋友交互
- 不要和陌生人说话
示例:
1// 违反迪米特法则2class Engine {3 public void start() {4 System.out.println("Engine started");5 }6}78class Car {9 private Engine engine;10 11 public Engine getEngine() {12 return engine;13 }14}1516class Driver {17 public void drive(Car car) {18 // 直接访问Car的内部对象19 car.getEngine().start();20 }21}2223// 符合迪米特法则24class Car {25 private Engine engine;26 27 // 封装内部细节28 public void start() {29 engine.start();30 }31}3233class Driver {34 public void drive(Car car) {35 // 只与Car交互36 car.start();37 }38}33. 谈谈你了解的最常见的几种设计模式,说说他们的应用场景
答案:
1. 单例模式
- 场景:数据库连接池、配置管理器、日志工具
- 优势:全局唯一实例,节省资源
2. 工厂模式
- 场景:对象创建逻辑复杂、需要解耦创建和使用
- 优势:封装创建逻辑,易于扩展
3. 代理模式
- 场景:Spring AOP、RPC调用、权限控制
- 优势:在不修改原对象的情况下增强功能
4. 观察者模式
- 场景:事件监听、消息队列、MVC架构
- 优势:松耦合的事件通知机制
5. 策略模式
- 场景:支付方式选择、排序算法切换
- 优势:算法可替换,避免大量if-else
6. 模板方法模式
- 场景:Servlet、Spring JdbcTemplate
- 优势:复用公共代码,子类只实现差异部分
7. 装饰器模式
- 场景:Java IO流、动态添加功能
- 优势:灵活扩展对象功能
8. 适配器模式
- 场景:系统集成、接口转换
- 优势:让不兼容的接口协同工作
34. 请用一句话概括,什么是设计模式?为什么要用?
答案:
什么是: 设计模式是软件开发中反复出现的问题的经验性解决方案。
为什么用:
- 提高代码质量:可读性、可维护性、可扩展性
- 降低开发成本:复用成熟方案,减少试错
- 统一开发语言:团队沟通更高效
- 应对变化:让系统更容易适应需求变化
- 最佳实践:站在巨人的肩膀上
一句话总结: 设计模式是前人智慧的结晶,帮助我们写出高质量、易维护、可扩展的代码。
35. 你认为好的代码应该是什么样的?
答案:
1. 可读性强
- 命名清晰、注释恰当
- 代码结构清晰
- 遵循编码规范
2. 可维护性好
- 低耦合、高内聚
- 职责单一
- 易于修改和扩展
3. 可测试性高
- 模块化设计
- 依赖可注入
- 易于编写单元测试
4. 性能优秀
- 算法高效
- 资源利用合理
- 无明显性能瓶颈
5. 健壮性强
- 异常处理完善
- 边界条件考虑周全
- 容错能力强
6. 遵循原则
- SOLID原则
- DRY(Don't Repeat Yourself)
- KISS(Keep It Simple, Stupid)
- YAGNI(You Aren't Gonna Need It)
代码示例:
1// 好的代码2public class OrderService {3 private final OrderRepository orderRepository;4 private final PaymentService paymentService;5 6 public OrderService(OrderRepository orderRepository, 7 PaymentService paymentService) {8 this.orderRepository = orderRepository;9 this.paymentService = paymentService;10 }11 12 public Order createOrder(OrderRequest request) {13 validateRequest(request);14 Order order = buildOrder(request);15 orderRepository.save(order);16 paymentService.processPayment(order);17 return order;18 }19 20 private void validateRequest(OrderRequest request) {21 if (request == null || request.getItems().isEmpty()) {22 throw new IllegalArgumentException("Invalid order request");23 }24 }25 26 private Order buildOrder(OrderRequest request) {27 // 构建订单逻辑28 return new Order();29 }30}36. 什么是合成复用原则?
答案:
合成复用原则(CRP):优先使用组合/聚合,而不是继承来达到复用目的。
核心思想:
- 继承是白盒复用,组合是黑盒复用
- 继承破坏封装性,组合保持封装性
- 继承是静态的,组合是动态的
示例:
1// 使用继承(不推荐)2class Animal {3 public void eat() {4 System.out.println("Eating");5 }6}78class Bird extends Animal {9 public void fly() {10 System.out.println("Flying");11 }12}1314class Penguin extends Bird {15 // 问题:企鹅不会飞,但继承了fly方法16 @Override17 public void fly() {18 throw new UnsupportedOperationException();19 }20}2122// 使用组合(推荐)23interface Eatable {24 void eat();25}2627interface Flyable {28 void fly();29}3031class EatBehavior implements Eatable {32 public void eat() {33 System.out.println("Eating");34 }35}3637class FlyBehavior implements Flyable {38 public void fly() {39 System.out.println("Flying");40 }41}4243class Bird {44 private Eatable eatBehavior;45 private Flyable flyBehavior;46 47 public Bird(Eatable eatBehavior, Flyable flyBehavior) {48 this.eatBehavior = eatBehavior;49 this.flyBehavior = flyBehavior;50 }51 52 public void eat() {53 eatBehavior.eat();54 }55 56 public void fly() {57 if (flyBehavior != null) {58 flyBehavior.fly();59 }60 }61}6263// 企鹅只有吃的行为,没有飞的行为64Bird penguin = new Bird(new EatBehavior(), null);优势:
- 更灵活:运行时可以改变行为
- 更松耦合:减少类之间的依赖
- 更易扩展:添加新功能不影响现有代码
学习指南
核心要点:
- 设计模式分类和应用场景
- SOLID 设计原则
- 常见设计模式的实现原理
- 设计模式在实际项目中的应用
学习路径建议:
- 掌握 SOLID 设计原则
- 熟悉三大类设计模式的特征
- 深入理解常见设计模式的实现
- 学习在实际项目中应用设计模式
评论区 / Comments