Java的OOP以类为唯一代码容器、对象为唯一运行实体,严格依托封装、继承、多态、抽象四大支柱;类是蓝图,对象是堆中独立实例;封装重在访问控制与逻辑校验,非仅private+getter/setter。

什么是类和对象?别把模板当实物
类是 class 声明的蓝图,对象是 new 出来的内存实例。二者关系就像“菜谱”和“做好的一盘红烧肉”:菜谱不占肚子,红烧肉能吃;类不占堆内存,对象才真分配空间。
- 类里定义的
private String name是字段,只描述“可能有姓名”,不存具体值 - 执行
Student s = new Student()时,JVM 才在堆上划一块内存,给s.name分配默认值(null或0) - 同一个类可以
new出十个对象,每个对象的name、age独立存储,互不影响
封装不是加个 private 就完事
封装的本质是「控制访问路径」,不是「藏起来就安全」。很多初学者写完 private 字段+public getter/setter 就以为封装完成了,但漏掉了关键逻辑校验。
-
setAge(int age)如果不检查age ,外部仍可传入 -5,导致业务语义崩坏 - getter 也可能要加逻辑:比如
getBalance()返回前先同步账户状态 - 字段暴露风险常发生在链式调用中:
student.getAddress().setCity("Beijing")——如果getAddress()返回的是内部对象引用,外部就能绕过封装直接改
public class Student {
private String name;
private Address address; // 注意:Address 是可变对象
public Address getAddress() {
return address; // ❌ 危险!返回了内部引用
}
public Address getAddressCopy() { // ✅ 正确做法:返回副本
return new Address(address.getCity(), address.getStreet());
}
}
继承不是为了“省几行代码”,而是建模 is-a 关系
用 extends 的前提是子类确实是父类的一种。强行继承(比如让 Car extends Engine)会破坏语义,后续多态和维护全崩。
- 子类必须能完全替代父类(Liskov 替换原则):调用
animal.eat(),不管传Dog还是Cat,行为都该是“进食”,不能一个抛异常一个静默 - 构造器链必须显式或隐式调用
super(),否则编译报错:Implicit super constructor Animal() is undefined - 不要在父类构造器里调用可被重写的方法(
this.speak()),子类字段此时还未初始化,容易 NPE
多态靠的是“编译看声明,运行看实际类型”
多态不是语法糖,是 JVM 在运行时查虚方法表(vtable)的结果。理解这点,才能避开空指针、类型强转失败、静态方法不参与多态等坑。
立即学习“Java免费学习笔记(深入)”;
- 变量声明类型决定“能调用哪些方法”(编译期检查),实际 new 的类型决定“执行哪个版本”(运行期绑定)
-
static方法不参与多态:即使子类定义同签名static speak(),调用仍是看左边类型 - 强制转型(
(Dog) animal)前务必用instanceof检查,否则ClassCastException直接崩溃
Animal a = new Dog(); a.eat(); // ✅ 调用 Dog.eat() —— 多态生效 a.sleep(); // ❌ 编译错误:Animal 类没有 sleep() 方法真正难的不是写出符合语法的 OOP 代码,而是每次加一个
private、写一个 extends、声明一个父类引用时,都得问自己一句:这个设计是否经得起“替换”“扩展”“误用”的三重检验。Java 的 OOP 规则很硬,绕不过去。










