日期: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
// 鼠标实现 Usb 接口
public class Mouse implements Usb {
@Override
public void startUsb() {
System.out.println("USB鼠标开启");
}
@Override
public void stopUsb() {
System.out.println("USB鼠标关闭");
}
}
// Cat 也实现 Usb 接口(演示用:任何类都可以实现接口)
public class Cat implements Usb {
@Override
public void startUsb() { }
@Override
public void stopUsb() { }
}
📄 来源:
Mouse.java、Cat.java
// 笔记本——方法参数为 Usb 接口类型
public class NoteBook {
public void useUsb(Usb usb) { // 📌 接口类型参数 —— 多态的体现
usb.startUsb();
System.out.println("开始工作.....");
usb.stopUsb();
}
}
📄 来源:
NoteBook.java
public class Test02 {
public static void main(String[] args) {
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接口的类都可以传入,无需修改笔记本的代码即可接入新设备——这就是 多态提高扩展性 的典型应用。
| 练习方向 | 涉及知识点 | 对应文件 |
|---|---|---|
| 多态入门 | 多态前提、父类引用指向子类对象、成员访问特点 | Test01.java、Dog.java |
| 多态中的转型 | 向上转型、向下转型、instanceof 运算符 | Demo02.java、Test03.java |
| 抽象类多态综合 | 抽象父类 Person、多态参数、向下转型调特有方法 | Test03.java、Person.java |
| 接口多态 | Usb 接口、笔记本接入多设备 | Test02.java、NoteBook.java |
instanceof 判断,否则可能抛出 ClassCastException(类型转换异常)。面向对象三大特征
├── 封装(private / getter / setter)
├── 继承(extends / super / @Override)
└── 多态 ⭐(今日重点)
├── 前提:继承/实现 + 重写 + 父类引用指向子类对象
├── 成员访问:编译看左边,运行看右边
├── 多态中的转型
│ ├── 向上转型(Upcasting)
│ │ └── 父类引用指向子类对象:Person p = new Student();
│ ├── 向下转型(Downcasting)
│ │ ├── 格式:子类类型 对象名 = (子类类型) 父类引用;
│ │ ├── 前提:必须先向上转型
│ │ └── 用途:调用子类特有方法
│ └── instanceof 运算符
│ └── 格式:对象名 instanceof 类型(返回 boolean)
└── 多态的好处:提高扩展性(开闭原则)
└── 案例:NoteBook.useUsb(Usb usb)