OOP是建模现实世界的思维习惯:拆解为对象、属性、方法并协作;封装用private+getter/setter控制数据;继承仅用于严格is-a关系,优先组合;多态受限于引用类型,需转型或抽象共性;抽象类表共有骨架,接口表共同能力。

面向对象编程(OOP)在Java中不是语法技巧,而是一套建模现实世界的思维习惯:把问题拆解成“谁”(对象)、“有什么”(属性)、“能干什么”(方法),再让它们协作完成任务。
封装:为什么不能直接 public 字段?
直接暴露 public 字段等于放弃对数据的控制权——别人可以随意赋值,比如给 age 设为 -5 或 200,而类自身毫无感知。封装的本质是“信任接口,不信任直接访问”。
- 用
private封住字段,强制所有修改走setter方法,在里面加校验逻辑 -
getter不只是读取,还能返回计算值(如getFullName()拼接姓和名),或返回不可变副本(避免外部篡改内部集合) - 别为了“省事”在 DTO 或 record 类里开
public字段——它们虽轻量,但一旦业务逻辑渗入,就失去约束力
继承:什么时候该用 extends,什么时候该组合?
继承表达的是严格的“is-a”关系,不是“has-a”或“uses-a”。滥用继承会导致类层次僵硬、语义失真,比如让 Circle 继承 Rectangle,只因都想画图——这违背常识,也违反里氏替换原则。
- 优先用组合:
Car有Engine(private Engine engine;),而不是Car extends Engine - 继承仅用于真正可替代的场景:比如
Dog和Cat都是Animal,调用animal.makeSound()时无需关心具体类型 - 子类重写方法时,必须保证行为契约不变——如果父类
withdraw(double)规定“余额不足抛InsufficientBalanceException”,子类不能静默失败或返回布尔值
多态:为什么父类引用调用子类方法,却总“看不见”子类特有功能?
这是多态的边界所在:编译期只认引用类型声明的方法签名。你用 Animal a = new Dog();,只能调用 Animal 中定义或继承的方法,哪怕 Dog 多了个 fetchBall(),a.fetchBall() 会直接编译报错。
立即学习“Java免费学习笔记(深入)”;
- 要调用子类特有方法,必须向下转型:
((Dog) a).fetchBall()——但得先instanceof判断,否则运行时抛ClassCastException - 更稳妥的做法是把共性行为抽象进父类或接口,比如让
Animal增加performTrick(),由各子类实现不同动作 - 接口多态比类继承更灵活:
List这种写法才是日常高频使用的多态形态list = new ArrayList();
抽象类 vs 接口:选哪个不是看“能不能写方法”,而是看“要不要强制共享状态”
抽象类可以含构造器、成员变量、protected 方法和默认实现;接口只能定义行为契约(Java 8+ 的 default 方法不能访问实例状态)。关键区别在于语义:抽象类表示“共有的骨架”,接口表示“共同的能力”。
- 需要共享字段或初始化逻辑?用抽象类,比如
AbstractDatabaseConnector含connectionUrl和统一连接池管理 - 多个不相关类要具备同一能力?用接口,比如
Runnable、Comparable、Serializable,连String和自定义类都能实现 - Java 8 后接口支持
static和default方法,但别把它当工具类用——default应是“通用默认行为”,不是“补丁式实现”
真正难的不是写出符合语法的 OOP 代码,而是判断某个需求到底该建模成对象、还是接口、还是纯函数;该用继承复用逻辑,还是用策略模式注入行为。这些没有标准答案,只取决于你对问题域的理解深度。











