面向对象三大特性概述
Java 面向对象的核心就三个词:封装、继承、多态
类比理解:
封装:把东西锁在保险箱里,只留一个小窗口给你操作(保护数据)
继承:儿子继承老爸的财产和基因,但可以有自己的特长(代码复用)
多态:同一句”叫”,狗汪汪叫、猫喵喵叫(同一个方法,不同表现)
建议先学完 类与对象,再来看这篇
封装(Encapsulation)
一句话理解:把字段藏起来,只通过方法来访问——就像 ATM 机,你不能直接伸手进去拿钱,必须通过按钮操作
为什么要封装
数据验证:setter 里可以加判断逻辑,比如年龄不能是负数
安全性:外部不能随便改内部数据
灵活性:内部实现可以随便改,只要接口不变,外部代码不受影响
从安全角度理解:封装 = 最小权限原则
只暴露必要的接口,隐藏一切不需要暴露的细节
就像公司的门禁系统——不是每个人都有服务器房间的钥匙,你只能进你该进的地方
这也是软件安全设计的基本思想之一
怎么做封装
第一步:字段用 private 修饰(锁起来)
第二步:提供 public 的 getter 和 setter 方法(留一个小窗口)
1 | public class BankAccount { |
1 |
|
访问修饰符一览
| 修饰符 | 本类 | 同包 | 子类 | 其他包 |
|---|---|---|---|---|
private |
✅ | ❌ | ❌ | ❌ |
| (默认/不写) | ✅ | ✅ | ❌ | ❌ |
protected |
✅ | ✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ | ✅ |
记忆口诀:private 最小,public 最大,protected 给儿子用
继承(Inheritance)
一句话理解:子类自动拥有父类的属性和方法——就像孩子继承了父母的基因,但还能长出自己的独特技能
extends 关键字
Java 用 extends 表示继承关系
语法:class 子类 extends 父类 { }
1 | // 父类(基类/超类) |
1 | // 子类(派生类) |
1 |
|
super 关键字
super 指向父类,和 this 指向自己是一对
两个用途:
super(参数) → 调用父类的构造器(必须放在子类构造器的第一行)
super.方法名() → 调用父类的方法(当子类重写了同名方法时,想调用原版就用 super)
1 | public class Cat extends Animal { |
方法重写(Override)vs 重载(Overload)对比
| 对比项 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 发生位置 | 父子类之间 | 同一个类里 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须相同 | 必须不同 |
| 返回值 | 相同或是子类类型 | 无要求 |
| 访问修饰符 | 子类 ≥ 父类 | 无要求 |
| 关键注解 | @Override |
无 |
| 目的 | 子类改写父类行为 | 提供方法的不同版本 |
重载的详细内容见 类与对象 中的方法重载章节
@Override 注解
加在重写的方法上面,不是必须的,但强烈建议加
好处:编译器会帮你检查,如果方法签名写错了(比如拼错方法名),立刻报错提醒你
1 |
|
Java 单继承
Java 只能 extends 一个类(不像 C++ 可以多继承)
为什么?避免”菱形继承”问题——如果爸爸和妈妈都有一个同名方法,儿子到底听谁的?
但 Java 可以 implements 多个接口(后面会学)
Object 是所有类的祖宗
所有类都默认继承 java.lang.Object,即使你没写 extends
所以每个对象都有 toString()、equals()、hashCode() 等方法——这些都是从 Object 继承来的
1 |
|
多态(Polymorphism)
一句话理解:同一个方法调用,不同对象有不同表现——你对所有动物说”叫”,狗汪汪叫、猫喵喵叫、鸭子嘎嘎叫
父类引用指向子类对象
这是多态的核心语法:Animal animal = new Dog();
左边是父类类型(Animal),右边是子类对象(Dog)
编译看左边,运行看右边:编译时检查父类有没有这个方法,运行时执行子类的版本
1 | public class Animal { |
1 |
|
向上转型和向下转型
向上转型(自动):子类 → 父类,安全,自动完成
Animal a = new Dog("旺财"); — Dog 自动变成 Animal 类型
向下转型(强制):父类 → 子类,危险,需要手动强转
Dog d = (Dog) a; — 必须确保 a 本来就是 Dog,否则报错 ClassCastException
用 instanceof 先判断再转,避免翻车
1 |
|
多态的好处
代码灵活:方法参数用父类类型,就能接收所有子类对象
可扩展:新增子类时,不需要改已有代码
解耦:调用方只依赖父类/接口,不关心具体实现
1 | // 多态的实际应用:方法参数用父类类型 |
1 |
|
instanceof 运算符
用来判断一个对象是不是某个类(或其子类)的实例
语法:对象 instanceof 类名,返回 boolean
什么时候用:在向下转型之前,先用 instanceof 检查,避免 ClassCastException
1 |
|
常见坑汇总
| 坑 | 说明 | 正确做法 |
|---|---|---|
| 字段没有多态 | Animal a = new Dog(); a.name 取的是父类的字段 |
用方法(getter)来访问,方法才有多态 |
| 向下转型不检查 | 直接 (Dog) animal 可能 ClassCastException |
先 instanceof 判断 |
| 子类构造器忘调 super | 如果父类没有无参构造器,子类必须显式 super(参数) |
第一行写 super(...) |
| 重写时缩小了访问权限 | 父类方法是 public,子类改成 private → 编译报错 | 子类权限 ≥ 父类 |
| 以为多态能调子类特有方法 | Animal a = new Dog(); a.fetch() 编译报错 |
先向下转型再调 |
| private 方法能被继承? | 不能! private 方法对子类不可见 | 用 protected 或 public |
练习题
练习1(封装):定义一个 User 类
私有属性:用户名(String)、密码(String)、年龄(int)
setter 中加验证:年龄必须在 0-150 之间,密码长度至少 6 位
写测试方法,尝试设置非法值,验证是否被拦截
练习2(继承 + 多态):设计一个动物类继承体系
父类 Animal:属性 name、方法 speak() 和 move()
子类 Dog:重写 speak() 输出”汪汪汪”,重写 move() 输出”四条腿跑”
子类 Bird:重写 speak() 输出”叽叽喳喳”,重写 move() 输出”展翅飞翔”
子类 Fish:重写 speak() 输出”…(鱼不会叫)”,重写 move() 输出”摇尾游泳”
写一个方法 public void showAnimal(Animal animal),传入不同动物,观察多态效果
练习3(综合思考题):下面的代码输出什么?
1 | Animal a = new Dog("旺财"); |
学完这篇,下一步可以了解抽象类和接口,那才是多态真正大展身手的地方
| 上一章 | 目录 | 下一章 |
|---|---|---|
| 类与对象 | java基础 | 访问修饰符 |