封装通过设字段为private、暴露必要public方法,使内部实现变更不影响调用方;继承需满足里氏替换原则,子类行为须与父类一致。

封装让修改范围可控
把字段设为 private,只暴露必要的 public 方法,不是为了“看起来规范”,而是当内部实现要改时,只要接口不变,调用方完全不用动。比如一个 Account 类的余额计算逻辑从简单加减改成带手续费的复合规则,只要 getBalance() 返回结果语义不变,所有用到它的业务代码都不用改。
容易踩的坑:
• 把 getter/setter 当成“必须配套生成”,结果暴露了不该被外部直接改的字段(如 status 应该由 approve() 或 reject() 控制)
• 在 setter 里不做校验,导致对象进入非法状态,后续运行才抛 NullPointerException 或业务异常
继承要满足里氏替换原则
子类能安全替换父类,关键不在语法能编译通过,而在行为一致。比如 PaymentProcessor 父类定义了 process(double amount),子类 AlipayProcessor 和 WechatProcessor 都必须能处理任意正数金额——如果某个子类对 amount 直接抛异常,而父类契约允许零值(用于退款冲正),那用多态调用就会出错。
实操建议:
• 抽象父类或接口只定义“做什么”,不规定“怎么做”
• 子类重写方法前,先看父类 Javadoc 是否明确写了前置/后置条件
• 避免在子类构造器里调用可被重写的方法,否则可能触发未初始化字段的逻辑
立即学习“Java免费学习笔记(深入)”;
多态降低模块间耦合
用接口类型声明变量、参数和返回值,而不是具体实现类。比如日志模块接收的是 Logger 接口,而不是 Log4jLogger,这样切换底层实现(SLF4J、Logback)时,业务代码一行不用动。
常见错误现象:
• 方法参数写成 ArrayList 而非 List,导致无法传入 LinkedList 或自定义集合实现
• 工厂方法返回具体类(如 new UserServiceImpl()),而不是接口(UserService),迫使调用方依赖实现细节
• 在 switch 或 if-else 中根据类型判断行为,其实应该用多态分发
public void handle(Order order) {
// ❌ 坏:类型判断破坏开闭原则
if (order instanceof SpecialOrder) {
((SpecialOrder) order).applyDiscount();
}
// ✅ 好:行为交给对象自己决定
order.process();
}
组合优于继承的实际约束
当“有一个”关系比“是一个”更贴切时,强行继承会快速劣化。比如 ReportGenerator 需要发送邮件,它不是一种 EmailSender,而是“有一个”发送器。用组合,就能随时替换成 MockEmailSender(测试)、AsyncEmailSender(性能优化)。
参数差异与影响:
• 继承:父类变更可能意外影响所有子类,尤其 protected 方法被重写时
• 组合:依赖对象通过构造器或 setter 注入,生命周期、线程安全、是否可为空都更明确
• 性能上无本质差异,但组合更容易做单元测试(可 mock 依赖)
容易忽略的一点:组合不是简单地 new 一个对象,而是要考虑依赖的创建时机和作用域。比如数据库连接池对象,应该作为成员变量复用,而不是每次方法调用都 new 一个新实例。










