如何通过Java代码实践体现设计原则面试高频题的应用?

摘要:设计原则面试高频题+Java代码实践(吃透面试+落地开发) 结合面试高频场景,我会先整理核心面试题+标准答案(覆盖90%面试考点),再用Java代码逐一演示七大设计原则的落地用法,兼顾“
设计原则面试高频题+Java代码实践(吃透面试+落地开发) 结合面试高频场景,我会先整理核心面试题+标准答案(覆盖90%面试考点),再用Java代码逐一演示七大设计原则的落地用法,兼顾“面试答题”和“实际开发”双重需求。 一、设计原则高频面试题+标准答案 基础必问(80%面试官会问) 1. 请说说SOLID五大设计原则分别是什么,各自的核心思想? 答案: SOLID是面向对象设计的核心原则,拆解为5个原则: 单一职责(SRP):一个类只负责一个职责,变更原因唯一,核心是“高内聚”,避免一个类承担过多功能导致修改牵一发而动全身; 开闭原则(OCP):对扩展开放、对修改关闭,核心是依赖抽象而非具体实现,新增功能通过扩展子类/实现类完成,不修改原有稳定代码; 里氏替换(LSP):子类可无缝替换父类且程序行为不变,核心是保证继承的合理性,子类不能破坏父类的约定(如不能重写父类非抽象方法、不能缩小方法的访问权限); 接口隔离(ISP):客户端不依赖不需要的接口,核心是将臃肿接口拆分为细粒度接口,避免接口变更影响无关客户端; 依赖倒置(DIP):高层模块不依赖低层模块,二者都依赖抽象;抽象不依赖细节,细节依赖抽象,核心是“面向接口编程”,解除模块间的强耦合。 2. 开闭原则是设计原则的核心目标,如何在代码中落地开闭原则? 答案: 开闭原则的核心是“抽象化”,落地关键有3点: 定义抽象层(接口/抽象类):将稳定的业务逻辑抽象为接口,封装不变的行为; 具体实现类扩展抽象层:新增功能时,编写新的实现类继承/实现抽象层,不修改原有实现; 依赖注入解耦:高层模块通过抽象层调用功能,而非直接依赖具体实现类。 举例:支付场景中,定义PayService接口,原有AlipayPayService实现支付宝支付,新增微信支付时,只需新增WechatPayService实现PayService,无需修改原有支付逻辑。 3. 里氏替换原则的核心要求是什么?举一个违反里氏替换的例子。 答案: 核心要求:所有引用父类的地方,替换为子类后程序的行为和逻辑不变,具体约束: 子类不能重写父类的非抽象方法; 子类不能扩大方法的前置条件(如父类方法参数是Object,子类不能改为String); 子类不能缩小方法的后置条件(如父类返回List,子类不能返回ArrayList并限制元素类型); 子类不能抛出父类未声明的异常(运行时异常除外)。 反例: // 父类:鸟类定义飞的行为 class Bird { public void fly() { System.out.println("鸟类飞行"); } } // 子类:鸵鸟,违反里氏替换 class Ostrich extends Bird { @Override public void fly() { throw new UnsupportedOperationException("鸵鸟不会飞"); } } // 调用方:依赖父类,但替换子类后报错 public class Test { public static void letBirdFly(Bird bird) { bird.fly(); } public static void main(String[] args) { letBirdFly(new Ostrich()); // 抛出异常,违反里氏替换 } } 修正方案:拆分抽象层,定义Flyable接口,只有会飞的鸟实现该接口,鸵鸟不实现。 4. 组合/聚合复用原则(CRP)和继承的区别?为什么优先组合? 答案: 维度 继承(is-a) 组合/聚合(has-a) 耦合程度 强耦合(父类变子类必变) 弱耦合(只需调整依赖) 复用灵活性 静态复用(编译期确定) 动态复用(运行时可替换) 代码冗余 易产生冗余(继承无关方法) 无冗余(按需依赖) 优先组合的原因: 避免继承的“侵入性”:子类会继承父类所有公开方法,即使不需要; 降低耦合:组合可在运行时替换依赖的对象,继承一旦确定无法修改; 符合开闭原则:新增功能只需替换组合的对象,无需修改原有类。 进阶扩展(面试加分题) 5. 迪米特法则(最少知识原则)如何落地?举代码例子说明。
阅读全文