Эх сурвалжийг харах

docs: 更新20260724课堂笔记,同步代码变更——新增多态中的转型(向上转型/向下转型/instanceof运算符)、Person→Student/Teacher综合案例(Test03.runPerson())

WanJL 23 цаг өмнө
parent
commit
5bf4ed7880

+ 2 - 2
README.md

@@ -31,7 +31,7 @@
 | 2026-07-21 | [构造方法:构造方法重载、this() 调用、private 构造方法、单例模式、课后作业回顾](./课堂笔记/01-JavaSE基础-笔记/20260721-笔记.md) |
 | 2026-07-22 | [继承:继承概念与格式、方法重写(@Override)、super 关键字、就近原则、继承中构造方法、单继承与多层继承、访问修饰符](./课堂笔记/01-JavaSE基础-笔记/20260722-笔记.md) |
 | 2026-07-23 | [抽象类 + final + 模板方法 + 代码块 + 接口(含JDK8+新特性):抽象类定义与特点、final 类/方法/变量、模板方法模式、三种代码块、接口定义与实现(Usb→LogiMouse/ROGMouse)、接口 vs 抽象类对比、JDK8默认方法static方法、JDK9私有方法](./课堂笔记/01-JavaSE基础-笔记/20260723-笔记.md) |
-| 2026-07-24 | [多态 + 接口进阶(JDK8默认方法/静态方法 + 模板方法设计模式):多态概念与前提、多态成员访问特点、多态的好处(开闭原则)、抽象类与多态结合、Usb接口多态案例(NoteBook接入鼠标/猫)、通知系统(Notifiable接口:default/static方法 + 模板方法send())](./课堂笔记/01-JavaSE基础-笔记/20260724-笔记.md) |
+| 2026-07-24 | [多态 + 多态中的转型 + 接口进阶(JDK8默认方法/静态方法 + 模板方法设计模式):多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof类型判断、多态的好处(开闭原则)、抽象类与多态结合(Person→Student/Teacher综合案例)、Usb接口多态案例(NoteBook接入鼠标/猫)、通知系统(Notifiable接口:default/static方法 + 模板方法send())](./课堂笔记/01-JavaSE基础-笔记/20260724-笔记.md) |
 
 ## 二、授课代码
 
@@ -49,7 +49,7 @@
 | 2026-07-21 | 构造方法:构造方法重载、this() 调用、private 构造方法、单例模式、Fisher-Yates 洗牌算法、课后作业回顾 | `c260721` | [`course`](./授课代码/c260721/src/course/) / [`homework/c260717`](./授课代码/c260721/src/homework/c260717/) / [`homework/c260720`](./授课代码/c260721/src/homework/c260720/) / [`exercise/exercise01`](./授课代码/c260721/src/exercise/exercise01/) / [`exercise/exercise02`](./授课代码/c260721/src/exercise/exercise02/) |
 | 2026-07-22 | 继承:继承概念与格式、方法重写(Override)、super 关键字、就近原则、继承中构造方法、单继承与多层继承、访问修饰符、综合练习(学生管理系统+冒泡排序)、课后作业 | `c260722` | [`course`](./授课代码/c260722/src/course/) / [`exercise/studentSystem`](./授课代码/c260722/src/exercise/studentSystem/) / [`homework/p08_guessgame`](./授课代码/c260722/src/homework/p08_guessgame/) / [`homework/p09_bookmanager`](./授课代码/c260722/src/homework/p09_bookmanager/) |
 | 2026-07-23 | 抽象类 + final + 模板方法 + 代码块 + 接口(含JDK8+新特性):抽象类定义与特点、final 类/方法/变量、模板方法模式(MyOneDayTemplate)、三种代码块(局部/构造/静态)、接口定义与实现(Usb→LogiMouse/ROGMouse)、接口 vs 抽象类对比、JDK8默认方法/静态方法(MyDefaultInterface→MyDefaultInterfaceImpl)、JDK9私有方法(抽取多个默认方法公共代码)、Test05 测试类 | `c260723` | [`course`](./授课代码/c260723/src/course/) |
-| 2026-07-24 | 多态概念与前提、多态成员访问特点、多态的好处(开闭原则)、抽象类+多态(Animal→Dog)、接口多态(Usb→Mouse/Cat→NoteBook)、接口JDK8默认方法/静态方法、模板方法设计模式(Notifiable→EmailNotifier→TestNotification) | `c260724` | [`course`](./授课代码/c260724/src/course/) / [`homework`](./授课代码/c260724/src/homework/) |
+| 2026-07-24 | 多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof运算符、多态的好处(开闭原则)、抽象类+多态(Animal→Dog、Person→Student/Teacher→Test03.runPerson())、接口多态(Usb→Mouse/Cat→NoteBook)、接口JDK8默认方法/静态方法、模板方法设计模式(Notifiable→EmailNotifier→TestNotification) | `c260724` | [`course`](./授课代码/c260724/src/course/) / [`homework`](./授课代码/c260724/src/homework/) |
 
 ## 三、课堂练习题
 

+ 59 - 0
授课代码/c260724/src/course/Demo02.java

@@ -0,0 +1,59 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo02
+ * @description 向下转型(向下造型)
+ * @create 2026/7/24
+ */
+public class Demo02 {
+    /*
+        多态中的转型
+            向上造型
+                父类的引用指向子类的对象
+            向下造型
+                子类的引用指向父类的对象
+                    格式: 子类类型 对象名=(子类类型)父类的引用名;
+
+                向下造型的前提一定是向上造型。
+                1、先要执行向上造型  :  父类类型 对象名=new 子类类型();
+                2、再执行向下造型   :   子类类型 子类对象名=父类对象名;
+                前提:Student extends Person
+
+                Person p=new Student();
+                Student s=(Student)p;
+
+                什么时候要使用向下造型呢?
+                    需要使用子类对象特有的方法。
+
+                比如父类:外卖类
+                    子类:麻辣烫类 、手撕烤鸭类、汉堡类
+
+                有这样一个方法:
+                public void 点外卖(外卖类 wm){
+
+                    //需要判断 什么样的外卖,执行什么样的操作
+                    //这时候就需要判断外卖类的对象名 wm到底指向的手撕烤鸭对象还是麻辣烫对象
+                    //并且需要进行向下转型 把外卖类转换为手撕烤鸭或麻辣烫
+                    //这时候就需要有一个运算符 来判断 这个 wm 对象 到底是 手撕烤鸭类,还是 麻辣烫类
+                    //使用 instanceof 运算符进行判断
+                    // 格式:  对象名 instanceof 类型
+                    // 通俗的理解: 判断 关键字 左边的变量,是否是属于 右边的类型,返回的是boolean类型结果
+                    // 运算结果 是一个布尔值
+                    if (wm instanceof 手撕烤鸭类){
+                        // 比如 转型为手撕烤鸭类:
+                        手撕烤鸭类 ssky=(手撕烤鸭类)wm;
+                        //调用手撕烤鸭类的特有方法
+                        ssky.准备塑料手套();
+                    }else if(wm instanceof 麻辣烫){
+                        // 比如 转型为麻辣烫类:
+                        麻辣烫类 mlt=(麻辣烫类)wm;
+                        mlt.准备筷子();
+                    }
+
+
+                }
+
+     */
+}

+ 15 - 0
授课代码/c260724/src/course/Person.java

@@ -0,0 +1,15 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Person
+ * @description
+ * @create 2026/7/24
+ */
+public abstract class Person {
+    public abstract void eat();
+
+    public abstract void sleep();
+
+}

+ 24 - 0
授课代码/c260724/src/course/Student.java

@@ -0,0 +1,24 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Student
+ * @description
+ * @create 2026/7/24
+ */
+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("学生学习...");
+    }
+}

+ 24 - 0
授课代码/c260724/src/course/Teacher.java

@@ -0,0 +1,24 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Teacher
+ * @description
+ * @create 2026/7/24
+ */
+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("教师玩....爽....");
+    }
+}

+ 42 - 0
授课代码/c260724/src/course/Test03.java

@@ -0,0 +1,42 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Test03
+ * @description
+ * @create 2026/7/24
+ */
+public class Test03 {
+
+    public void runPerson(Person p){
+        p.eat();    //调用 子类对象 重写 Person类的eat()方法
+        //判断p对象属于哪种类型的对象,根据不同的对象,调用不同子类的特有方法
+        if (p instanceof Student){
+            //向下造型  Person p  -> Student
+            Student s=(Student)p;
+            //这时候就可以调用s特有的方法
+            s.study();
+        }else if(p instanceof Teacher){
+            //向下造型  Person p  -> Teacher
+            Teacher t=(Teacher) p;
+            //这时候就可以调用t特有的方法
+            t.play();
+        }
+        p.sleep();    //调用 子类对象 重写 Person类的sleep()方法
+
+    }
+
+    public static void main(String[] args) {
+        Test03 t=new Test03();
+        //创建子类的对象
+        Student s=new Student();
+        t.runPerson(s);
+
+        //直接把上面的两步合成一步
+        t.runPerson(new Teacher());
+    }
+
+
+
+}

+ 160 - 3
课堂笔记/01-JavaSE基础-笔记/20260724-笔记.md

@@ -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 新特性