日期:2026-07-24
项目:c260724
作者:WanJL
面向对象三大特征之一(封装、继承、多态)。
同一个对象,在不同时刻表现出来的不同形态。
例如:
张三 是 Student 类型Student extends Person,则张三同时具备 Student 和 Person 两种形态Person extends Animal,则张三具备 Student、Person、Animal 三种形态核心理解:一个对象可以拥有多种类型身份,通过父类/接口的引用指向子类/实现类的对象。
| 条件 | 说明 |
|---|---|
| ① 继承或实现关系 | 子类 extends 父类,或实现类 implements 接口 |
| ② 方法重写 | 子类/实现类必须重写父类/接口的方法 |
| ③ 父类引用指向子类对象 | Animal a = new Dog(); |
// 父类引用指向子类对象
Animal animal = new Dog();
animal.eat();
animal.sleep();
📄 来源:
Test01.java
| 成员类型 | 编译时期 | 运行时期 |
|---|---|---|
| 成员变量 | 看父类 | 看父类(没有多态) |
| 成员方法 | 看父类(需存在) | 看子类(重写后执行子类) |
口诀:编译看左边,运行看右边(针对方法而言)。
// 仅能调用父类中声明的方法
Animal animal = new Dog();
animal.eat(); // ✅ 编译看父类有 eat(),运行执行 Dog 的 eat()
animal.sleep(); // ✅ 编译看父类有 sleep()(抽象),运行执行 Dog 的 sleep()
// animal.wangWang(); // ❌ 编译报错,父类没有 wangWang()
📄 来源:
Test01.java、Dog.java
多态让代码对扩展开放,对修改关闭(开闭原则)。
典型应用:笔记本 USB 接口案例
// Usb 接口
public interface Usb {
void startUsb();
void stopUsb();
}
// 笔记本类——接收任意 Usb 实现类
public class NoteBook {
public void useUsb(Usb usb) { // 参数类型为 Usb 接口
usb.startUsb();
System.out.println("开始工作.....");
usb.stopUsb();
}
}
// 鼠标实现 Usb
public class Mouse implements Usb {
public void startUsb() { System.out.println("USB鼠标开启"); }
public void stopUsb() { System.out.println("USB鼠标关闭"); }
}
📄 来源:
Usb.java、NoteBook.java、Mouse.java
// 测试多态
NoteBook noteBook = new NoteBook();
Usb mouse = new Mouse();
noteBook.useUsb(mouse); // ✅ 传入鼠标
Usb cat = new Cat(); // ✅ 猫也能实现 Usb(示例演示)
noteBook.useUsb(cat); // ✅ 传入猫 USB
📄 来源:
Test02.java关键点:
NoteBook.useUsb(Usb usb)的参数是接口类型,任何实现Usb接口的类都可以传入,无需修改笔记本代码即可扩展新设备。
多态涉及两种类型转换:
即"父类的引用指向子类的对象",前面所有示例都属于向上转型。
Person p = new Student(); // ✅ 向上转型
Animal a = new Dog(); // ✅ 向上转型
Usb mouse = new Mouse(); // ✅ 向上转型(接口)
概念:将父类引用 强制转换 为子类引用,恢复子类对象的完整形态。
格式:
子类类型 对象名 = (子类类型) 父类引用名;
前提:必须先执行向上转型。
// 1、先向上转型
Person p = new Student();
// 2、再向下转型
Student s = (Student) p; // ✅ 强制转换
为什么需要向下转型?
若需调用子类特有方法(如 study()、play()),必须向下转型
Person p = new Student();
p.eat(); // ✅ 父类中有 eat()
p.sleep(); // ✅ 父类中有 sleep()
// p.study(); // ❌ 编译报错!父类中没有 study()
Student s = (Student) p; // 向下转型
s.study(); // ✅ 转型后可以调用子类特有方法
📄 来源:
Demo02.java
问题:向下转型前,如何确定父类引用指向的是哪个子类对象?直接强转可能引发 ClassCastException。
解决:使用 instanceof 运算符进行类型判断。
| 格式 | 说明 |
|---|---|
对象名 instanceof 类型 |
判断左边对象是否属于右边类型 |
返回 boolean |
true 表示是,false 表示否 |
// 生活案例:外卖点餐
public void 点外卖(外卖类 wm) {
if (wm instanceof 手撕烤鸭类) {
手撕烤鸭类 ssky = (手撕烤鸭类) wm;
ssky.准备塑料手套();
} else if (wm instanceof 麻辣烫类) {
麻辣烫类 mlt = (麻辣烫类) wm;
mlt.准备筷子();
}
}
📄 来源:
Demo02.java
将多态、instanceof、向下转型三者结合使用:
public class Test03 {
// 参数为抽象父类类型——多态的体现
public void runPerson(Person p) {
p.eat(); // ✅ 编译看Person,运行看子类
// 判断 p 指向的具体子类类型
if (p instanceof Student) {
Student s = (Student) p; // 向下转型
s.study(); // 调用子类特有方法
} else if (p instanceof Teacher) {
Teacher t = (Teacher) p; // 向下转型
t.play(); // 调用子类特有方法
}
p.sleep(); // ✅ 编译看Person,运行看子类
}
public static void main(String[] args) {
Test03 t = new Test03();
t.runPerson(new Student()); // 向上转型传入 Student
t.runPerson(new Teacher()); // 向上转型传入 Teacher
}
}
📄 来源:
Test03.java
执行流程分析(以传入 new Student() 为例):
runPerson(new Student())
─→ p.eat() → 输出 "学生吃饭..."
─→ p instanceof Student → true → (Student)p → s.study() → 输出 "学生学习..."
─→ p.sleep() → 输出 "学生睡觉..."
public abstract class Animal {
public void eat() { // 普通方法
System.out.println("动物吃饭.....");
}
public abstract void sleep(); // 抽象方法
}
📄 来源:
Animal.java
public class Dog extends Animal {
@Override
public void sleep() {
System.out.println("小狗睡觉....");
}
@Override
public void eat() {
System.out.println("小狗吃饭....");
}
public void wangWang() {
System.out.println("小狗汪汪....");
}
}
📄 来源:
Dog.java
eat())wangWang())// 抽象父类
public abstract class Person {
public abstract void eat();
public abstract void sleep();
}
// 子类 Student
public class Student extends Person {
@Override
public void eat() { System.out.println("学生吃饭..."); }
@Override
public void sleep(){ System.out.println("学生睡觉..."); }
public void study(){ System.out.println("学生学习..."); } // 特有方法
}
// 子类 Teacher
public class Teacher extends Person {
@Override
public void eat() { System.out.println("教师吃饭..."); }
@Override
public void sleep(){ System.out.println("教师睡觉..."); }
public void play() { System.out.println("教师玩....爽...."); } // 特有方法
}
📄 来源:
Person.java、Student.java、Teacher.java
public interface Usb {
void startUsb(); // 抽象方法(默认 public abstract)
void stopUsb();
}
📄 来源:
Usb.java
在接口中定义带方法体的方法,使用 default 关键字。实现类可以选择重写或不重写。
public interface Notifiable {
// 抽象方法——实现类必须重写
void doSend(String recipient, String message);
// 默认方法——校验参数(实现类可选重写)
default boolean validateParams(String recipient, String message) {
if (recipient != null && message != null && !recipient.isEmpty() && !message.isEmpty()) {
System.out.println("参数校验通过");
return true;
}
System.out.println("参数校验失败:收件人或消息不能为空");
return false;
}
// 默认方法——记录日志(实现类可选重写)
default void log(String level, String msg) {
System.out.println("[" + level + "]消息:" + msg);
}
}
📄 来源:
Notifiable.java
使用 static 关键字定义,通过接口名直接调用。
public interface Notifiable {
static void printNotificationType() {
System.out.println("通知系统 v1.0——支持邮件/短信/微信通知");
}
}
// 调用方式
Notifiable.printNotificationType();
📄 来源:
Notifiable.java、TestNotification.java
在接口中定义一个 默认方法 作为模板,规定固定的算法流程,让实现类只需关注核心业务逻辑。
public interface Notifiable {
// 模板方法——定义固定的发送流程
default void send(String recipient, String message) {
// 1、校验参数
boolean boo = validateParams(recipient, message);
if (boo) {
// 2、执行发送(调用抽象方法,由实现类完成具体逻辑)
doSend(recipient, message);
// 3、记录日志
log("INFO", "发送成功");
System.out.println("--- 发送流程结束 ---");
}
}
}
📄 来源:
Notifiable.java
流程示意图:
send() → validateParams() → doSend() → log() → 完成
(模板) (默认方法-校验) (抽象方法) (默认方法)
使用 JDK8 新特性定义通知接口:
doSend()——实现类实现不同的发送逻辑validateParams()——参数校验log()——日志记录send()——模板方法,组合流程printNotificationType()——输出系统信息public class EmailNotifier implements Notifiable {
@Override
public void doSend(String recipient, String message) {
System.out.println("发送邮件给 " + recipient + ":内容 -" + message);
}
// 可选重写默认方法 log()
@Override
public void log(String level, String msg) {
System.out.println("[邮件][" + level + "]消息:" + msg);
}
}
📄 来源:
EmailNotifier.java
public class TestNotification {
public static void main(String[] args) {
// 1、调用接口静态方法
Notifiable.printNotificationType();
// 2、创建实现类对象
EmailNotifier en = new EmailNotifier();
// 3、正常发送
en.send("admin@163.com", "系统升级通知");
// 4、参数为空——触发校验失败
en.send("", "测试");
}
}
📄 来源:
TestNotification.java
| 练习方向 | 涉及知识点 | 对应文件 |
|---|---|---|
| 多态入门 | 多态前提、父类引用指向子类对象、成员访问特点 | Test01.java、Dog.java |
| 多态中的转型 | 向上转型、向下转型、instanceof 运算符 | Demo02.java、Test03.java |
| 抽象类多态综合 | 抽象父类 Person、多态参数、向下转型调特有方法 | Test03.java、Person.java |
| 接口多态 | Usb 接口、笔记本接入多设备 | Test02.java、NoteBook.java |
| 通知系统(作业) | 接口默认方法、静态方法、模板方法设计模式 | 作业包 homework |
instanceof 判断,否则可能抛出 ClassCastException(类型转换异常)。面向对象三大特征
├── 封装(private / getter / setter)
├── 继承(extends / super / @Override)
└── 多态 ⭐(今日重点)
├── 前提:继承/实现 + 重写 + 父类引用指向子类对象
├── 成员访问:编译看左边,运行看右边
├── 多态中的转型
│ ├── 向上转型(Upcasting)
│ │ └── 父类引用指向子类对象:Person p = new Student();
│ ├── 向下转型(Downcasting)
│ │ ├── 格式:子类类型 对象名 = (子类类型) 父类引用;
│ │ ├── 前提:必须先向上转型
│ │ └── 用途:调用子类特有方法
│ └── instanceof 运算符
│ └── 格式:对象名 instanceof 类型(返回 boolean)
├── 多态的好处:提高扩展性(开闭原则)
│ └── 案例:NoteBook.useUsb(Usb usb)
└── 接口多态 + JDK8 新特性
├── 默认方法(default)—— 模板方法设计模式
└── 静态方法(static)—— 接口名直接调用