فهرست منبع

feat: 新增20260723抽象类课堂练习题——Shape抽象类(计算周长+面积)/Instrument乐器体系/Payment支付系统/模板方法设计模式

WanJL 1 روز پیش
والد
کامیت
8afad8d4b9
2فایلهای تغییر یافته به همراه420 افزوده شده و 0 حذف شده
  1. 1 0
      README.md
  2. 419 0
      课堂练习题/20260723-抽象类练习.md

+ 1 - 0
README.md

@@ -58,4 +58,5 @@
 | 2026-07-20 | [面向对象基础练习](./课堂练习题/20260720-面向对象基础练习.md) | 类与对象、封装、包、方法、数组、Random 综合
 | 2026-07-20 | [面向对象基础练习](./课堂练习题/20260720-面向对象基础练习.md) | 类与对象、封装、包、方法、数组、Random 综合
 | 2026-07-21 | [构造方法练习](./课堂练习题/20260721-构造方法练习.md) | 构造方法概念、构造方法重载、private 构造方法、this() 构造方法间调用、构造方法+对象数组+排序+Random 综合 |
 | 2026-07-21 | [构造方法练习](./课堂练习题/20260721-构造方法练习.md) | 构造方法概念、构造方法重载、private 构造方法、this() 构造方法间调用、构造方法+对象数组+排序+Random 综合 |
 | 2026-07-22 | [继承练习](./课堂练习题/20260722-继承练习.md) | 继承概念与格式、多层继承、super() 调用父类构造方法、方法重写、is...a 关系判断、继承+构造方法+对象数组+排序+多态综合 |
 | 2026-07-22 | [继承练习](./课堂练习题/20260722-继承练习.md) | 继承概念与格式、多层继承、super() 调用父类构造方法、方法重写、is...a 关系判断、继承+构造方法+对象数组+排序+多态综合 |
+| 2026-07-23 | [抽象类练习](./课堂练习题/20260723-抽象类练习.md) | 抽象类定义与特点、抽象方法重写、抽象类计算周长与面积(Shape体系)、抽象类+对象数组+排序+多态综合、模板方法设计模式 |
 
 

+ 419 - 0
课堂练习题/20260723-抽象类练习.md

@@ -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 | 课外 | 模板方法设计模式(抽象类经典应用) | ⭐⭐⭐⭐⭐ |