|
@@ -0,0 +1,419 @@
|
|
|
|
|
+# 2026-07-23 抽象类(Abstract)基础练习(课堂练习用)
|
|
|
|
|
+
|
|
|
|
|
+> **说明**:以下习题围绕今日授课核心内容——**抽象类(abstract)** 展开,涵盖抽象类的定义、特点、抽象方法的重写以及抽象类与对象数组/排序的综合应用。
|
|
|
|
|
+>
|
|
|
|
|
+> 题目分为三部分:**随堂练习**(抽象类入门,4 题)、**进阶练习**(综合挑战,2 题)和 **课外拓展**(1 题)。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+# 第一部分:随堂练习
|
|
|
|
|
+
|
|
|
|
|
+> 以下练习围绕抽象类的基础概念和用法,由浅入深。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 练习 1:抽象类入门——Instrument 乐器体系
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐
|
|
|
|
|
+**知识点**:抽象类的定义、抽象方法、子类重写抽象方法
|
|
|
|
|
+
|
|
|
|
|
+创建一个乐器抽象体系:
|
|
|
|
|
+
|
|
|
|
|
+**`Instrument` 抽象类**(父类):
|
|
|
|
|
+- **抽象方法**:`play()` —— 演奏乐器(无返回值、无参数)
|
|
|
|
|
+
|
|
|
|
|
+**`Piano` 类**(继承 Instrument):
|
|
|
|
|
+- **重写** `play()` —— 输出 `"弹钢琴:叮咚叮咚~"`
|
|
|
|
|
+
|
|
|
|
|
+**`Violin` 类**(继承 Instrument):
|
|
|
|
|
+- **重写** `play()` —— 输出 `"拉小提琴:咿呀咿呀~"`
|
|
|
|
|
+
|
|
|
|
|
+**`Drum` 类**(继承 Instrument):
|
|
|
|
|
+- **重写** `play()` —— 输出 `"打鼓:咚咚咚!"`
|
|
|
|
|
+
|
|
|
|
|
+**测试类要求**:
|
|
|
|
|
+1. 创建 `Piano`、`Violin`、`Drum` 各一个对象
|
|
|
|
|
+2. 分别调用它们的 `play()` 方法
|
|
|
|
|
+3. 观察:相同的方法名 `play()` 在不同的子类中有不同的行为——这就是 **抽象方法多态** 的体现
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**:
|
|
|
|
|
+```
|
|
|
|
|
+弹钢琴:叮咚叮咚~
|
|
|
|
|
+拉小提琴:咿呀咿呀~
|
|
|
|
|
+打鼓:咚咚咚!
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 练习 2:形状抽象类——Shape 图形体系 ⭐(必做)
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐⭐
|
|
|
|
|
+**知识点**:抽象类定义、抽象方法(计算周长 + 计算面积)、子类重写所有抽象方法
|
|
|
|
|
+
|
|
|
|
|
+创建一个图形抽象体系,要求抽象父类中定义 **计算周长** 和 **计算面积** 的抽象方法。
|
|
|
|
|
+
|
|
|
|
|
+**`Shape` 抽象类**(父类):
|
|
|
|
|
+- **私有属性**:`color`(颜色,String)
|
|
|
|
|
+- **有参构造方法**:`Shape(String color)`
|
|
|
|
|
+- **抽象方法**:`double getArea()` —— 计算面积
|
|
|
|
|
+- **抽象方法**:`double getPerimeter()` —— 计算周长
|
|
|
|
|
+- **普通方法**:`showInfo()` —— 输出 `"颜色:xxx,面积:xxx,周长:xxx"`(通过调用 `getArea()` 和 `getPerimeter()` 获得)
|
|
|
|
|
+
|
|
|
|
|
+**`Circle` 类**(继承 Shape):
|
|
|
|
|
+- **新增私有属性**:`radius`(半径,double)
|
|
|
|
|
+- **构造方法**:`Circle(String color, double radius)`
|
|
|
|
|
+- **重写** `getArea()` —— 返回 `π × r²`(π 取 `3.1415926`)
|
|
|
|
|
+- **重写** `getPerimeter()` —— 返回 `2 × π × r`
|
|
|
|
|
+
|
|
|
|
|
+**`Rectangle` 类**(继承 Shape):
|
|
|
|
|
+- **新增私有属性**:`width`(宽度,double)、`height`(高度,double)
|
|
|
|
|
+- **构造方法**:`Rectangle(String color, double width, double height)`
|
|
|
|
|
+- **重写** `getArea()` —— 返回 `width × height`
|
|
|
|
|
+- **重写** `getPerimeter()` —— 返回 `2 × (width + height)`
|
|
|
|
|
+
|
|
|
|
|
+**`Triangle` 类**(继承 Shape):
|
|
|
|
|
+- **新增私有属性**:`a`(边长 a,double)、`b`(边长 b,double)、`c`(边长 c,double)
|
|
|
|
|
+- **构造方法**:`Triangle(String color, double a, double b, double c)`
|
|
|
|
|
+- **重写** `getArea()` —— 使用海伦公式:`s = (a+b+c)/2`,面积 = `√(s(s-a)(s-b)(s-c))`(`Math.sqrt()` 求平方根)
|
|
|
|
|
+- **重写** `getPerimeter()` —— 返回 `a + b + c`
|
|
|
|
|
+
|
|
|
|
|
+**测试类要求**:
|
|
|
|
|
+1. 创建 `Circle` 对象("红色", 5.0),调用 `showInfo()` 输出信息
|
|
|
|
|
+2. 创建 `Rectangle` 对象("蓝色", 4.0, 6.0),调用 `showInfo()` 输出信息
|
|
|
|
|
+3. 创建 `Triangle` 对象("绿色", 3.0, 4.0, 5.0),调用 `showInfo()` 输出信息
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**(保留两位小数):
|
|
|
|
|
+```
|
|
|
|
|
+颜色:红色,面积:78.54,周长:31.42
|
|
|
|
|
+颜色:蓝色,面积:24.00,周长:20.00
|
|
|
|
|
+颜色:绿色,面积:6.00,周长:12.00
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+> **提示**:
|
|
|
|
|
+> - 子类构造方法中必须调用 `super(color)` 初始化父类的 color 属性
|
|
|
|
|
+> - `Triangle` 的面积使用海伦公式:`double s = (a + b + c) / 2; double area = Math.sqrt(s * (s - a) * (s - b) * (s - c));`
|
|
|
|
|
+> - 输出时使用 `System.out.printf("%.2f", value)` 保留两位小数
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 练习 3:员工抽象类——Employee 工作体系
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐⭐
|
|
|
|
|
+**知识点**:抽象类、抽象方法重写、构造方法 + super()
|
|
|
|
|
+
|
|
|
|
|
+创建一个员工工作体系:
|
|
|
|
|
+
|
|
|
|
|
+**`Employee` 抽象类**(父类):
|
|
|
|
|
+- **私有属性**:`name`(姓名,String)、`baseSalary`(基本工资,double)
|
|
|
|
|
+- **有参构造方法**:`Employee(String name, double baseSalary)`
|
|
|
|
|
+- **抽象方法**:`void work()` —— 工作内容
|
|
|
|
|
+- **抽象方法**:`double calculateSalary()` —— 计算实际薪资
|
|
|
|
|
+- **普通方法**:`showInfo()` —— 输出 `"姓名:xxx,薪资:xxx"`
|
|
|
|
|
+
|
|
|
|
|
+**`Manager` 类**(继承 Employee):
|
|
|
|
|
+- **新增私有属性**:`bonus`(岗位津贴,double)
|
|
|
|
|
+- **构造方法**:`Manager(String name, double baseSalary, double bonus)`
|
|
|
|
|
+- **重写** `work()` —— 输出 `"经理在管理团队"`
|
|
|
|
|
+- **重写** `calculateSalary()` —— 返回 `baseSalary + bonus`
|
|
|
|
|
+
|
|
|
|
|
+**`Programmer` 类**(继承 Employee):
|
|
|
|
|
+- **新增私有属性**:`projectBonus`(项目奖金,double)
|
|
|
|
|
+- **构造方法**:`Programmer(String name, double baseSalary, double projectBonus)`
|
|
|
|
|
+- **重写** `work()` —— 输出 `"程序员在编写代码"`
|
|
|
|
|
+- **重写** `calculateSalary()` —— 返回 `baseSalary + projectBonus`
|
|
|
|
|
+
|
|
|
|
|
+**测试类要求**:
|
|
|
|
|
+1. 创建 `Manager` 对象("张经理", 15000, 5000),调用 `work()` 和 `showInfo()`
|
|
|
|
|
+2. 创建 `Programmer` 对象("李编程", 12000, 3000),调用 `work()` 和 `showInfo()`
|
|
|
|
|
+3. 思考:如果将 `Employee` 改为非抽象类,把 `work()` 和 `calculateSalary()` 改为普通方法,会有什么问题?
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**:
|
|
|
|
|
+```
|
|
|
|
|
+经理在管理团队
|
|
|
|
|
+姓名:张经理,薪资:20000.0
|
|
|
|
|
+程序员在编写代码
|
|
|
|
|
+姓名:李编程,薪资:15000.0
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 练习 4:判断抽象类设计——哪些类应该定义为抽象类
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐⭐
|
|
|
|
|
+**知识点**:抽象类设计思想、何时使用抽象类
|
|
|
|
|
+
|
|
|
|
|
+判断以下场景中,哪些类 **应该** 定义为抽象类,哪些 **不应该**,并说明理由。
|
|
|
|
|
+
|
|
|
|
|
+| 序号 | 类名 | 描述 | 是否抽象? | 理由 |
|
|
|
|
|
+|------|------|------|-----------|------|
|
|
|
|
|
+| 1 | `Animal` | 有 `move()` 方法,所有子类移动方式不同 | | |
|
|
|
|
|
+| 2 | `Car` | 有 `start()`、`stop()` 方法,所有汽车启动和停止方式相同 | | |
|
|
|
|
|
+| 3 | `Payment` | 有 `pay()` 方法,微信支付/支付宝/信用卡支付逻辑不同 | | |
|
|
|
|
|
+| 4 | `Student` | 有 `study()` 方法,小学生和大学生学习方式不同,但也有共性属性(学号、姓名) | | |
|
|
|
|
|
+| 5 | `MathUtils` | 只有静态工具方法 `max()`、`min()`、`abs()`,不需要子类 | | |
|
|
|
|
|
+| 6 | `Database` | 有 `connect()`、`disconnect()`、`executeQuery()`,MySQL/Oracle 实现不同 | | |
|
|
|
|
|
+
|
|
|
|
|
+**参考答案思路**:
|
|
|
|
|
+
|
|
|
|
|
+| 序号 | 是否抽象? | 理由 |
|
|
|
|
|
+|------|-----------|------|
|
|
|
|
|
+| 1 | ✅ 应该 | 不同动物的移动方式完全不同(走/飞/游),父类无法给出通用实现 |
|
|
|
|
|
+| 2 | ❌ 不应 | 所有汽车的启动和停止方式基本一致,用普通父类即可 |
|
|
|
|
|
+| 3 | ✅ 应该 | 不同支付方式的 `pay()` 逻辑完全不同 |
|
|
|
|
|
+| 4 | ✅ 应该 | 不同阶段学生学习方式不同,但`study()` 作为统一接口定义是合理的 |
|
|
|
|
|
+| 5 | ❌ 不应 | 工具类不需要继承体系,不应定义为抽象类 |
|
|
|
|
|
+| 6 | ✅ 应该 | 不同数据库的连接和查询方式不同,适合用抽象类定义规范 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+# 第二部分:进阶练习
|
|
|
|
|
+
|
|
|
|
|
+> 以下为综合挑战题,融合抽象类与对象数组、排序、多态等知识点。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 第 1 题:抽象类 + 对象数组 + 排序——Shape 图形面积排序 ⭐⭐⭐⭐
|
|
|
|
|
+
|
|
|
|
|
+综合运用抽象类、对象数组、冒泡排序、多态等知识点。
|
|
|
|
|
+
|
|
|
|
|
+### 需求说明
|
|
|
|
|
+
|
|
|
|
|
+利用 **练习 2** 中定义的 `Shape` 抽象类及其子类(`Circle`、`Rectangle`、`Triangle`),创建一个图形数组,按 **面积从大到小** 排序。
|
|
|
|
|
+
|
|
|
|
|
+### 类设计
|
|
|
|
|
+
|
|
|
|
|
+直接复用练习 2 中的 `Shape`(抽象类)、`Circle`、`Rectangle`、`Triangle` 类。
|
|
|
|
|
+
|
|
|
|
|
+### 测试类要求
|
|
|
|
|
+
|
|
|
|
|
+**`ShapeSortTest` 类**:
|
|
|
|
|
+
|
|
|
|
|
+1. **创建图形数组**:创建一个 `Shape[]` 数组,包含至少 5 个不同的图形对象
|
|
|
|
|
+
|
|
|
|
|
+ **示例数据**:
|
|
|
|
|
+ ```java
|
|
|
|
|
+ Shape s1 = new Circle("红色", 5.0); // 面积 ≈ 78.54
|
|
|
|
|
+ Shape s2 = new Rectangle("蓝色", 4.0, 6.0); // 面积 = 24.0
|
|
|
|
|
+ Shape s3 = new Triangle("绿色", 3.0, 4.0, 5.0); // 面积 = 6.0
|
|
|
|
|
+ Shape s4 = new Circle("黄色", 3.0); // 面积 ≈ 28.27
|
|
|
|
|
+ Shape s5 = new Rectangle("紫色", 8.0, 2.0); // 面积 = 16.0
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+2. **遍历输出所有图形信息**:调用每个元素的 `showInfo()` 方法
|
|
|
|
|
+
|
|
|
|
|
+3. **按面积从大到小排序**:使用冒泡排序,按 `getArea()` 返回值降序排列
|
|
|
|
|
+
|
|
|
|
|
+4. **排序后再次输出所有图形信息**
|
|
|
|
|
+
|
|
|
|
|
+5. **查找面积最大的图形**:遍历数组,输出面积最大的图形信息
|
|
|
|
|
+
|
|
|
|
|
+### 思考题
|
|
|
|
|
+
|
|
|
|
|
+1. 数组类型声明为 `Shape[]`,为什么能存储 `Circle`、`Rectangle`、`Triangle` 等不同类型的对象?这体现了 OOP 的哪个特征?
|
|
|
|
|
+2. 如果新增一个 `Square`(正方形)类,它应该继承谁?需要重写哪些方法?
|
|
|
|
|
+3. 抽象类 `Shape` 能否直接 `new Shape("黑色")` 创建对象?为什么?
|
|
|
|
|
+
|
|
|
|
|
+### 预期输出(参考)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+========== 排序前 ==========
|
|
|
|
|
+颜色:红色,面积:78.54,周长:31.42
|
|
|
|
|
+颜色:蓝色,面积:24.00,周长:20.00
|
|
|
|
|
+颜色:绿色,面积:6.00,周长:12.00
|
|
|
|
|
+颜色:黄色,面积:28.27,周长:18.85
|
|
|
|
|
+颜色:紫色,面积:16.00,周长:20.00
|
|
|
|
|
+
|
|
|
|
|
+========== 排序后(按面积降序) ==========
|
|
|
|
|
+颜色:红色,面积:78.54,周长:31.42
|
|
|
|
|
+颜色:黄色,面积:28.27,周长:18.85
|
|
|
|
|
+颜色:蓝色,面积:24.00,周长:20.00
|
|
|
|
|
+颜色:紫色,面积:16.00,周长:20.00
|
|
|
|
|
+颜色:绿色,面积:6.00,周长:12.00
|
|
|
|
|
+
|
|
|
|
|
+========== 面积最大的图形 ==========
|
|
|
|
|
+颜色:红色,面积:78.54,周长:31.42
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+> **提示**:
|
|
|
|
|
+> - 所有子类构造方法中都需要调用 `super(color)` 初始化父类属性
|
|
|
|
|
+> - `Shape[]` 数组可以存储任意 `Shape` 子类的对象——这是 **多态** 的体现
|
|
|
|
|
+> - 排序时使用冒泡排序,比较的是 `getArea()` 的返回值
|
|
|
|
|
+> - 面积/周长计算使用 `double` 类型,输出时使用 `System.out.printf("%.2f", value)`
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 第 2 题:抽象类 + 多态——支付系统 Payment ⭐⭐⭐⭐⭐
|
|
|
|
|
+
|
|
|
|
|
+### 需求说明
|
|
|
|
|
+
|
|
|
|
|
+开发一个支付系统,支持多种支付方式。每种支付方式的 **支付逻辑不同**,但都有 **支付** 和 **退款** 两个核心行为。
|
|
|
|
|
+
|
|
|
|
|
+### 类设计
|
|
|
|
|
+
|
|
|
|
|
+**`Payment` 抽象类**(父类):
|
|
|
|
|
+- **私有属性**:`amount`(支付金额,double)、`orderId`(订单号,String)
|
|
|
|
|
+- **有参构造方法**:`Payment(double amount, String orderId)`
|
|
|
|
|
+- **抽象方法**:`void pay()` —— 执行支付
|
|
|
|
|
+- **抽象方法**:`void refund()` —— 执行退款
|
|
|
|
|
+- **普通方法**:`showPaymentInfo()` —— 输出 `"订单号:xxx,金额:xxx,支付方式:xxx"`
|
|
|
|
|
+
|
|
|
|
|
+**`WeChatPay` 类**(继承 Payment):
|
|
|
|
|
+- **新增私有属性**:`openId`(微信用户标识,String)
|
|
|
|
|
+- **构造方法**:`WeChatPay(double amount, String orderId, String openId)`
|
|
|
|
|
+- **重写** `pay()` —— 输出 `"微信支付:扫描二维码,扣款 xxx 元"`
|
|
|
|
|
+- **重写** `refund()` —— 输出 `"微信退款:原路退回 xxx 元"`
|
|
|
|
|
+- **重写** `showPaymentInfo()` —— 输出 `"订单号:xxx,金额:xxx,支付方式:微信支付"`
|
|
|
|
|
+
|
|
|
|
|
+**`AliPay` 类**(继承 Payment):
|
|
|
|
|
+- **新增私有属性**:`account`(支付宝账号,String)
|
|
|
|
|
+- **构造方法**:`AliPay(double amount, String orderId, String account)`
|
|
|
|
|
+- **重写** `pay()` —— 输出 `"支付宝支付:跳转支付宝,扣款 xxx 元"`
|
|
|
|
|
+- **重写** `refund()` —— 输出 `"支付宝退款:原路退回 xxx 元"`
|
|
|
|
|
+- **重写** `showPaymentInfo()` —— 输出 `"订单号:xxx,金额:xxx,支付方式:支付宝"`
|
|
|
|
|
+
|
|
|
|
|
+**`CreditCard` 类**(继承 Payment):
|
|
|
|
|
+- **新增私有属性**:`cardNumber`(信用卡号,String)
|
|
|
|
|
+- **构造方法**:`CreditCard(double amount, String orderId, String cardNumber)`
|
|
|
|
|
+- **重写** `pay()` —— 输出 `"信用卡支付:刷卡扣款 xxx 元,额度已冻结"`
|
|
|
|
|
+- **重写** `refund()` —— 输出 `"信用卡退款:解冻额度,退回 xxx 元"`
|
|
|
|
|
+- **重写** `showPaymentInfo()` —— 输出 `"订单号:xxx,金额:xxx,支付方式:信用卡(尾号xxxx)"`(只显示信用卡后4位,使用 `cardNumber.substring(cardNumber.length() - 4)`)
|
|
|
|
|
+
|
|
|
|
|
+### 测试类要求
|
|
|
|
|
+
|
|
|
|
|
+**`PaymentTest` 类**:
|
|
|
|
|
+
|
|
|
|
|
+1. **创建支付对象数组**:创建一个 `Payment[]` 数组,包含至少 4 笔不同的支付记录
|
|
|
|
|
+
|
|
|
|
|
+ **示例数据**:
|
|
|
|
|
+ ```java
|
|
|
|
|
+ Payment p1 = new WeChatPay(99.9, "ORDER001", "wx_openid_123");
|
|
|
|
|
+ Payment p2 = new AliPay(299.0, "ORDER002", "alipay@example.com");
|
|
|
|
|
+ Payment p3 = new CreditCard(5999.0, "ORDER003", "6222021234567890");
|
|
|
|
|
+ Payment p4 = new WeChatPay(15.5, "ORDER004", "wx_openid_456");
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+2. **遍历执行支付**:遍历数组,对每个元素调用 `pay()` 和 `showPaymentInfo()` 方法
|
|
|
|
|
+
|
|
|
|
|
+3. **遍历执行退款**:遍历数组,对每个元素调用 `refund()` 方法
|
|
|
|
|
+
|
|
|
|
|
+4. **统计总支付金额**:遍历数组,累加每个元素的 `amount`,输出总金额
|
|
|
|
|
+
|
|
|
|
|
+### 思考题
|
|
|
|
|
+
|
|
|
|
|
+1. 如果新增一个 `ApplePay` 类,需要做哪些工作?
|
|
|
|
|
+2. 如果 `pay()` 和 `refund()` 不是抽象方法,而是在父类中写一个默认实现,会有什么弊端?
|
|
|
|
|
+3. 在实际项目中,抽象类常用于定义「**规范**」。让所有子类遵循相同的命名和参数,有什么好处?
|
|
|
|
|
+
|
|
|
|
|
+### 预期输出(参考)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+========== 执行支付 ==========
|
|
|
|
|
+微信支付:扫描二维码,扣款 99.9 元
|
|
|
|
|
+订单号:ORDER001,金额:99.9,支付方式:微信支付
|
|
|
|
|
+支付宝支付:跳转支付宝,扣款 299.0 元
|
|
|
|
|
+订单号:ORDER002,金额:299.0,支付方式:支付宝
|
|
|
|
|
+信用卡支付:刷卡扣款 5999.0 元,额度已冻结
|
|
|
|
|
+订单号:ORDER003,金额:5999.0,支付方式:信用卡(尾号7890)
|
|
|
|
|
+微信支付:扫描二维码,扣款 15.5 元
|
|
|
|
|
+订单号:ORDER004,金额:15.5,支付方式:微信支付
|
|
|
|
|
+
|
|
|
|
|
+========== 执行退款 ==========
|
|
|
|
|
+微信退款:原路退回 99.9 元
|
|
|
|
|
+支付宝退款:原路退回 299.0 元
|
|
|
|
|
+信用卡退款:解冻额度,退回 5999.0 元
|
|
|
|
|
+微信退款:原路退回 15.5 元
|
|
|
|
|
+
|
|
|
|
|
+========== 支付统计 ==========
|
|
|
|
|
+总支付金额:6413.4 元
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+> **提示**:
|
|
|
|
|
+> - 所有子类构造方法中都需要调用 `super(amount, orderId)` 初始化父类属性
|
|
|
|
|
+> - `showPaymentInfo()` 在父类中已经声明为普通方法(非抽象),子类可以选择重写也可以不重写。这里要求 **重写** 以便输出不同的支付方式名称
|
|
|
|
|
+> - 抽象类的意义在于:**定义统一规范,强制子类实现特定行为**
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+# 第三部分:课外拓展
|
|
|
|
|
+
|
|
|
|
|
+## 第 3 题:模板方法设计模式——抽象类经典应用 ⭐⭐⭐⭐⭐
|
|
|
|
|
+
|
|
|
|
|
+> **说明**:模板方法模式(Template Method Pattern)是抽象类的经典应用场景。**父类定义算法的「骨架」,子类实现特定的「步骤」**。
|
|
|
|
|
+
|
|
|
|
|
+### 需求说明
|
|
|
|
|
+
|
|
|
|
|
+模拟一个「在线考试系统」,每门考试都需要经过以下步骤:
|
|
|
|
|
+1. **登录验证**(所有考试相同)
|
|
|
|
|
+2. **发放试卷**(所有考试相同)
|
|
|
|
|
+3. **考试进行中**(不同科目考试内容不同)← **抽象方法**
|
|
|
|
|
+4. **收卷评分**(不同科目评分标准不同)← **抽象方法**
|
|
|
|
|
+5. **公布成绩**(所有考试相同)
|
|
|
|
|
+
|
|
|
|
|
+### 类设计
|
|
|
|
|
+
|
|
|
|
|
+**`ExamTemplate` 抽象类**(父类):
|
|
|
|
|
+- **普通方法** `login()` —— 输出 `"考生登录系统,验证身份... 登录成功!"`
|
|
|
|
|
+- **普通方法** `deliverPaper()` —— 输出 `"发放试卷,考试开始!"`
|
|
|
|
|
+- **抽象方法** `void conductExam()` —— 考试进行中(不同科目不同)
|
|
|
|
|
+- **抽象方法** `void gradeExam()` —— 收卷评分(不同科目不同)
|
|
|
|
|
+- **普通方法** `announceResult()` —— 输出 `"成绩公布!请登录系统查看"`
|
|
|
|
|
+- **模板方法** `final void startExam()` —— 按顺序执行完整考试流程:
|
|
|
|
|
+ 1. 调用 `login()`
|
|
|
|
|
+ 2. 调用 `deliverPaper()`
|
|
|
|
|
+ 3. 调用 `conductExam()`
|
|
|
|
|
+ 4. 调用 `gradeExam()`
|
|
|
|
|
+ 5. 调用 `announceResult()`
|
|
|
|
|
+
|
|
|
|
|
+**`JavaExam` 类**(继承 ExamTemplate):
|
|
|
|
|
+- **重写** `conductExam()` —— 输出 `"Java 考试进行中:编写代码,回答选择题..."`
|
|
|
|
|
+- **重写** `gradeExam()` —— 输出 `"Java 考试评分:检查代码正确性,选择题自动判分..."`
|
|
|
|
|
+
|
|
|
|
|
+**`MathExam` 类**(继承 ExamTemplate):
|
|
|
|
|
+- **重写** `conductExam()` —— 输出 `"数学考试进行中:解答微积分、线性代数题目..."`
|
|
|
|
|
+- **重写** `gradeExam()` —— 输出 `"数学考试评分:答案比对,步骤给分..."`
|
|
|
|
|
+
|
|
|
|
|
+### 测试类要求
|
|
|
|
|
+
|
|
|
|
|
+**`ExamTest` 类**:
|
|
|
|
|
+
|
|
|
|
|
+1. 创建 `JavaExam` 对象,调用 `startExam()` 方法
|
|
|
|
|
+2. 创建 `MathExam` 对象,调用 `startExam()` 方法
|
|
|
|
|
+3. 观察:**考试流程的「骨架」由父类定义,子类只需实现「考试内容」和「评分方式」两个步骤**——这就是模板方法模式
|
|
|
|
|
+
|
|
|
|
|
+### 预期输出
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+========== Java 考试 ==========
|
|
|
|
|
+考生登录系统,验证身份... 登录成功!
|
|
|
|
|
+发放试卷,考试开始!
|
|
|
|
|
+Java 考试进行中:编写代码,回答选择题...
|
|
|
|
|
+Java 考试评分:检查代码正确性,选择题自动判分...
|
|
|
|
|
+成绩公布!请登录系统查看
|
|
|
|
|
+
|
|
|
|
|
+========== 数学考试 ==========
|
|
|
|
|
+考生登录系统,验证身份... 登录成功!
|
|
|
|
|
+发放试卷,考试开始!
|
|
|
|
|
+数学考试进行中:解答微积分、线性代数题目...
|
|
|
|
|
+数学考试评分:答案比对,步骤给分...
|
|
|
|
|
+成绩公布!请登录系统查看
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 思考题
|
|
|
|
|
+
|
|
|
|
|
+1. `startExam()` 方法为什么用 `final` 修饰?如果不加 `final` 会怎样?
|
|
|
|
|
+2. 模板方法模式的核心思想是什么?和「普通继承 + 方法重写」有什么区别?
|
|
|
|
|
+3. 你在日常生活中有哪些场景可以用模板方法模式来描述?(例如:做饭流程、工作日报提交流程等)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 📋 题目总览
|
|
|
|
|
+
|
|
|
|
|
+| 序号 | 所属部分 | 核心知识点 | 难度 |
|
|
|
|
|
+|------|---------|-----------|------|
|
|
|
|
|
+| 练习 1 | 随堂 | 抽象类定义、抽象方法、子类重写抽象方法 | ⭐ |
|
|
|
|
|
+| 练习 2 | 随堂 | **抽象类:计算周长 + 计算面积**、方法重写、super() 调用父类构造方法 | ⭐⭐ |
|
|
|
|
|
+| 练习 3 | 随堂 | 抽象类 + 构造方法 + super()、抽象方法重写 | ⭐⭐ |
|
|
|
|
|
+| 练习 4 | 随堂 | 抽象类设计思想、判断何时使用抽象类 | ⭐⭐ |
|
|
|
|
|
+| 进阶 1 | 进阶 | 抽象类 + 对象数组 + 冒泡排序 + 多态 | ⭐⭐⭐⭐ |
|
|
|
|
|
+| 进阶 2 | 进阶 | 抽象类 + 多态 + 继承 + 综合应用 | ⭐⭐⭐⭐⭐ |
|
|
|
|
|
+| 拓展 3 | 课外 | 模板方法设计模式(抽象类经典应用) | ⭐⭐⭐⭐⭐ |
|