# 1. 开闭原则(OCP)的扩展性边界中通过继承 vs 通过组合的取舍 A 继承是唯一实现 OCP 的方式 B 组合与 OCP 无关 C OCP 要求修改既有代码来扩展 D 组合加接口抽象通常比继承更能实现"对扩展开放、对修改关闭" ✓ 正确答案
# 2. Liskov Substitution Principle(LSP)——子类型必须能够替换父类型而不改变程序正确性 A 子类可以随意改变基类方法的语义 B 子类型必须能替换父类型不破坏程序正确性 ✓ 正确答案 C LSP 只与接口命名有关 D 违反 LSP 没有后果
# 3. Contract-based substitutability(契约可替换)中前置条件、后置条件、不变量的继承规则 A 子类前置条件只能放宽、后置条件只能加强、不变量必须保持 ✓ 正确答案 B 子类前置条件应收窄,后置条件应削弱 C 契约与 LSP 无关 D 子类可以破坏父类不变量
# 4. LSP 的违反反例中正方形继承矩形(resize 行为变化)、子类抛异常、缩小前置条件 A 子类方法比父类更严格地满足前置条件 B 子类只增加方法 C 子类保持父类不变量 D 正方形继承矩形,resize 时行为与矩形契约不一致 ✓ 正确答案
# 5. Sealed class(封闭类)作为可控的多态边界(Java 17、Kotlin、Scala 3) A 它允许任意类继承以扩展 B 它只适用于 Kotlin 语言 C 它与多态无关 D 它限定允许的子类集合,配合模式匹配实现穷尽性检查 ✓ 正确答案
# 6. OCP 的「测试开放」维度中如何用契约测试(Contract Test)守护扩展点,使新增实现不破坏既有行为,并把契约测试纳入 CI 门禁? A 契约测试只能手工运行 B 契约测试只用于 UI 测试 C 契约测试为接口定义通用行为,所有实现都必须通过,可纳入 CI 门禁 ✓ 正确答案 D 新增实现不需要遵守契约测试
# 7. 协变(covariance)与逆变(contravariance)在函数参数的 LSP 边界 A 子类方法参数应逆变(更宽),返回类型应协变(更具体) ✓ 正确答案 B 参数应协变,返回应逆变 C 参数与返回类型无关 D 逆变违反 LSP
# 8. Override 的可见性限制(Java 不能缩窄、C# 同样不允许) A Java 允许覆写时缩窄可见性 B C# 覆写时可以自由改变访问级别 C Java 不能缩窄可见性以维护 LSP ✓ 正确答案 D 可见性与 LSP 无关
# 9. 返回类型协变(return type covariance)的 LSP 安全 A 返回类型协变与安全无关 B 返回类型协变会破坏 LSP C Java 不支持返回类型协变 D 子类返回更具体类型是 LSP 安全的 ✓ 正确答案
# 11. LSP 与「集合协变」的工程陷阱中 Java 数组协变与泛型不变性(List<Dog> 不是 List<Animal>)如何导致运行期错误,设计上应如何规避? A 数组协变把类型错误拖到运行期,泛型不变性在编译期拦截 ✓ 正确答案 B 泛型也是协变的,与数组等价 C List<Dog> 是 List<Animal> 的子类型 D 应尽量用数组协变
# 12. 异常抛出协变(throws covariance)的争议 A 异常协变完全没有任何争议 B 子类必须抛与父类完全相同的异常 C 非受检异常受 throws 约束 D Java 允许子类抛比父类更窄的受检异常 ✓ 正确答案
# 14. LSP 与继承设计中契约式设计(pre/post condition)? A 前置条件可缩窄,后置条件可削弱 B 契约只适用于接口 C 随意破坏契约 D 前置条件只放宽、后置条件只加强、不变量保持 ✓ 正确答案
# 15. OCP 的权衡中何时扩展点过度设计? A 为所有方法都预留扩展点是最佳实践 B 扩展点永远没有成本 C 投机式抽象(为虚假未来预造接口)是过度设计 ✓ 正确答案 D 只要有接口就一定符合 OCP
# 16. LSP 与接口隔离的配合? A ISP 通过拆细接口,减少子类被迫空实现/抛异常,从而帮助满足 LSP ✓ 正确答案 B 两者互相对立 C 胖接口有利于 LSP D ISP 与 LSP 无关