|
|
@@ -105,6 +105,121 @@ noteBook.useUsb(cat); // ✅ 传入猫 USB
|
|
|
|
|
|
---
|
|
|
|
|
|
+### 1.5 多态中的转型
|
|
|
+
|
|
|
+多态涉及两种类型转换:
|
|
|
+
|
|
|
+#### 1.5.1 向上转型(Upcasting)
|
|
|
+
|
|
|
+即"父类的引用指向子类的对象",前面所有示例都属于向上转型。
|
|
|
+
|
|
|
+```java
|
|
|
+Person p = new Student(); // ✅ 向上转型
|
|
|
+Animal a = new Dog(); // ✅ 向上转型
|
|
|
+Usb mouse = new Mouse(); // ✅ 向上转型(接口)
|
|
|
+```
|
|
|
+
|
|
|
+#### 1.5.2 向下转型(Downcasting)
|
|
|
+
|
|
|
+**概念**:将父类引用 **强制转换** 为子类引用,恢复子类对象的完整形态。
|
|
|
+
|
|
|
+**格式**:
|
|
|
+```java
|
|
|
+子类类型 对象名 = (子类类型) 父类引用名;
|
|
|
+```
|
|
|
+
|
|
|
+**前提**:必须先执行向上转型。
|
|
|
+
|
|
|
+```java
|
|
|
+// 1、先向上转型
|
|
|
+Person p = new Student();
|
|
|
+// 2、再向下转型
|
|
|
+Student s = (Student) p; // ✅ 强制转换
|
|
|
+```
|
|
|
+
|
|
|
+**为什么需要向下转型?**
|
|
|
+- 向上转型后,父类引用**只能调用父类中声明的方法**,无法调用子类的**特有方法**
|
|
|
+- 若需调用子类特有方法(如 `study()`、`play()`),必须向下转型
|
|
|
+
|
|
|
+```java
|
|
|
+Person p = new Student();
|
|
|
+p.eat(); // ✅ 父类中有 eat()
|
|
|
+p.sleep(); // ✅ 父类中有 sleep()
|
|
|
+// p.study(); // ❌ 编译报错!父类中没有 study()
|
|
|
+
|
|
|
+Student s = (Student) p; // 向下转型
|
|
|
+s.study(); // ✅ 转型后可以调用子类特有方法
|
|
|
+```
|
|
|
+> 📄 来源:`Demo02.java`
|
|
|
+
|
|
|
+#### 1.5.3 instanceof 运算符——类型判断
|
|
|
+
|
|
|
+**问题**:向下转型前,如何确定父类引用指向的是哪个子类对象?直接强转可能引发 `ClassCastException`。
|
|
|
+
|
|
|
+**解决**:使用 `instanceof` 运算符进行类型判断。
|
|
|
+
|
|
|
+| 格式 | 说明 |
|
|
|
+|------|------|
|
|
|
+| `对象名 instanceof 类型` | 判断左边对象是否属于右边类型 |
|
|
|
+| 返回 `boolean` | `true` 表示是,`false` 表示否 |
|
|
|
+
|
|
|
+```java
|
|
|
+// 生活案例:外卖点餐
|
|
|
+public void 点外卖(外卖类 wm) {
|
|
|
+ if (wm instanceof 手撕烤鸭类) {
|
|
|
+ 手撕烤鸭类 ssky = (手撕烤鸭类) wm;
|
|
|
+ ssky.准备塑料手套();
|
|
|
+ } else if (wm instanceof 麻辣烫类) {
|
|
|
+ 麻辣烫类 mlt = (麻辣烫类) wm;
|
|
|
+ mlt.准备筷子();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+> 📄 来源:`Demo02.java`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 1.6 综合案例:runPerson() 方法
|
|
|
+
|
|
|
+将多态、`instanceof`、向下转型三者结合使用:
|
|
|
+
|
|
|
+```java
|
|
|
+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() → 输出 "学生睡觉..."
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
## 二、抽象类回顾与多态结合
|
|
|
|
|
|
### 2.1 抽象类定义
|
|
|
@@ -142,6 +257,35 @@ public class Dog extends Animal {
|
|
|
- 子类可以重写父类的普通方法(`eat()`)
|
|
|
- 子类可以扩展自己的特有方法(`wangWang()`)
|
|
|
|
|
|
+### 2.3 另一组示例:Person → Student / Teacher
|
|
|
+
|
|
|
+```java
|
|
|
+// 抽象父类
|
|
|
+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`
|
|
|
+
|
|
|
---
|
|
|
|
|
|
## 三、接口多态与接口新特性(JDK8+)
|
|
|
@@ -284,6 +428,8 @@ public class TestNotification {
|
|
|
| 练习方向 | 涉及知识点 | 对应文件 |
|
|
|
|---------|-----------|---------|
|
|
|
| 多态入门 | 多态前提、父类引用指向子类对象、成员访问特点 | `Test01.java`、`Dog.java` |
|
|
|
+| 多态中的转型 | 向上转型、向下转型、instanceof 运算符 | `Demo02.java`、`Test03.java` |
|
|
|
+| 抽象类多态综合 | 抽象父类 Person、多态参数、向下转型调特有方法 | `Test03.java`、`Person.java` |
|
|
|
| 接口多态 | Usb 接口、笔记本接入多设备 | `Test02.java`、`NoteBook.java` |
|
|
|
| 通知系统(作业) | 接口默认方法、静态方法、模板方法设计模式 | 作业包 `homework` |
|
|
|
|
|
|
@@ -294,9 +440,11 @@ public class TestNotification {
|
|
|
1. **多态的实现原理**——Java 中多态是通过**动态绑定(后期绑定)**实现的,JVM 在运行时根据对象的实际类型调用对应的方法。
|
|
|
2. **多态与继承的关系**——多态是基于继承或接口实现的。
|
|
|
3. **多态的优势**——提高代码的可扩展性和可维护性,符合**开闭原则**(对扩展开放,对修改关闭)。
|
|
|
-4. **接口默认方法的用途**——在不破坏已有实现类的前提下向接口添加新方法;实现模板方法模式。
|
|
|
-5. **接口静态方法的用途**——提供工具方法,接口即命名空间,无需额外工具类。
|
|
|
-6. **模板方法设计模式**——定义一个操作中的算法骨架(模板方法),将一些步骤延迟到子类/实现类中实现。
|
|
|
+4. **向下转型的风险**——向下转型必须配合 `instanceof` 判断,否则可能抛出 `ClassCastException`(类型转换异常)。
|
|
|
+5. **instanceof 的用法**——常用于向下转型前的安全判断,避免强制类型转换异常。
|
|
|
+6. **接口默认方法的用途**——在不破坏已有实现类的前提下向接口添加新方法;实现模板方法模式。
|
|
|
+7. **接口静态方法的用途**——提供工具方法,接口即命名空间,无需额外工具类。
|
|
|
+8. **模板方法设计模式**——定义一个操作中的算法骨架(模板方法),将一些步骤延迟到子类/实现类中实现。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -309,6 +457,15 @@ public class TestNotification {
|
|
|
└── 多态 ⭐(今日重点)
|
|
|
├── 前提:继承/实现 + 重写 + 父类引用指向子类对象
|
|
|
├── 成员访问:编译看左边,运行看右边
|
|
|
+ ├── 多态中的转型
|
|
|
+ │ ├── 向上转型(Upcasting)
|
|
|
+ │ │ └── 父类引用指向子类对象:Person p = new Student();
|
|
|
+ │ ├── 向下转型(Downcasting)
|
|
|
+ │ │ ├── 格式:子类类型 对象名 = (子类类型) 父类引用;
|
|
|
+ │ │ ├── 前提:必须先向上转型
|
|
|
+ │ │ └── 用途:调用子类特有方法
|
|
|
+ │ └── instanceof 运算符
|
|
|
+ │ └── 格式:对象名 instanceof 类型(返回 boolean)
|
|
|
├── 多态的好处:提高扩展性(开闭原则)
|
|
|
│ └── 案例:NoteBook.useUsb(Usb usb)
|
|
|
└── 接口多态 + JDK8 新特性
|