|
|
@@ -1,8 +1,8 @@
|
|
|
# 2026-07-24 多态(Polymorphism)基础练习(课堂练习用)
|
|
|
|
|
|
-> **说明**:以下习题围绕今日授课核心内容——**多态(Polymorphism)** 展开,涵盖多态的概念与前提、多态成员访问特点、向上转型/向下转型、instanceof 运算符、多态与抽象类/接口的结合、接口默认方法/静态方法以及模板方法设计模式。
|
|
|
+> **说明**:以下习题围绕今日授课核心内容——**多态(Polymorphism)** 展开,涵盖多态的概念与前提、多态成员访问特点、向上转型/向下转型、instanceof 运算符、多态与抽象类/接口的结合。
|
|
|
>
|
|
|
-> 题目分为三部分:**随堂练习**(多态基础,4 题)、**进阶练习**(综合挑战,2 题)和 **课后作业**(接口默认方法/静态方法 + 模板方法,2 题)。
|
|
|
+> 题目分为两部分:**随堂练习**(多态基础,4 题)和 **进阶练习**(综合挑战,2 题)。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -445,291 +445,6 @@ public void workAll() {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
----
|
|
|
-
|
|
|
-# 第三部分:课后作业
|
|
|
-
|
|
|
-> 以下作业围绕接口 JDK8 新特性(默认方法、静态方法)和模板方法设计模式,结合课堂通知系统案例进行扩展。
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 作业 1:接口默认方法 + 静态方法——消息通知系统扩展
|
|
|
-
|
|
|
-**难度**:⭐⭐⭐
|
|
|
-**知识点**:接口默认方法(default)、接口静态方法(static)、implements 实现接口、方法重写
|
|
|
-
|
|
|
-### 需求说明
|
|
|
-
|
|
|
-在课堂 `Notifiable` 通知系统基础上,增加更多的通知方式。要求使用 **接口默认方法** 提供通用的日志记录和参数校验功能,使用 **接口静态方法** 提供工具方法。
|
|
|
-
|
|
|
-### 接口定义
|
|
|
-
|
|
|
-**`Notifiable` 接口**(可直接复用课堂代码,或自行重新定义):
|
|
|
-- **抽象方法**:`void doSend(String recipient, String message)`
|
|
|
-- **默认方法**:`default boolean validateParams(...)` —— 校验参数
|
|
|
-- **默认方法**:`default void log(String level, String msg)` —— 记录日志
|
|
|
-- **默认方法**:`default void send(String recipient, String message)` —— 模板方法(固定发送流程)
|
|
|
-- **静态方法**:`static void printNotificationType()` —— 输出系统信息
|
|
|
-
|
|
|
-### 要求实现以下 3 个通知类
|
|
|
-
|
|
|
-**`SMSNotifier` 类**(实现 Notifiable):
|
|
|
-- **重写** `doSend()` —— 输出 `"发送短信给 xxx:内容 -xxx"`
|
|
|
-- **不重写** `log()` —— 使用接口默认实现
|
|
|
-- **不重写** `validateParams()` —— 使用接口默认实现
|
|
|
-
|
|
|
-**`WeChatNotifier` 类**(实现 Notifiable):
|
|
|
-- **私有属性**:`appId`(应用 ID,String)
|
|
|
-- **构造方法**:`WeChatNotifier(String appId)`
|
|
|
-- **重写** `doSend()` —— 输出 `"发送微信消息给 xxx(appId: xxx):内容 -xxx"`
|
|
|
-- **重写** `log()` —— 输出 `"[微信][xxx]消息:xxx"`(添加 [微信] 前缀)
|
|
|
-
|
|
|
-**`StationNotifier` 类**(实现 Notifiable,**站内信通知**):
|
|
|
-- **私有属性**:`platformName`(平台名称,String)
|
|
|
-- **构造方法**:`StationNotifier(String platformName)`
|
|
|
-- **重写** `doSend()` —— 输出 `"[xxx] 站内信发送给 xxx:内容 -xxx"`
|
|
|
-- **重写** `log()` —— 输出 `"[站内信][xxx]消息:xxx"`
|
|
|
-
|
|
|
-### 测试类要求
|
|
|
-
|
|
|
-**`NotificationTest` 类**:
|
|
|
-
|
|
|
-```java
|
|
|
-public class NotificationTest {
|
|
|
- public static void main(String[] args) {
|
|
|
- // 1. 调用接口静态方法
|
|
|
- Notifiable.printNotificationType();
|
|
|
-
|
|
|
- // 2. 创建不同通知方式的对象
|
|
|
- Notifiable sms = new SMSNotifier();
|
|
|
- Notifiable wechat = new WeChatNotifier("wx_app_001");
|
|
|
- Notifiable station = new StationNotifier("企业办公平台");
|
|
|
-
|
|
|
- // 3. 发送通知
|
|
|
- System.out.println("--- 短信通知 ---");
|
|
|
- sms.send("13800138000", "您的验证码是 123456");
|
|
|
-
|
|
|
- System.out.println("--- 微信通知 ---");
|
|
|
- wechat.send("user_openid_123", "您有一条新消息");
|
|
|
-
|
|
|
- System.out.println("--- 站内信通知 ---");
|
|
|
- station.send("全体员工", "下午 3 点会议室开会");
|
|
|
-
|
|
|
- // 4. 测试空参数——触发校验失败
|
|
|
- System.out.println("--- 空参数测试 ---");
|
|
|
- sms.send("", "test");
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-### 预期输出
|
|
|
-
|
|
|
-```
|
|
|
-通知系统 v1.0——支持邮件/短信/微信通知
|
|
|
---- 短信通知 ---
|
|
|
-参数校验通过
|
|
|
-发送短信给 13800138000:内容 -您的验证码是 123456
|
|
|
-[INFO]消息:发送成功
|
|
|
---- 发送流程结束 ---
|
|
|
---- 微信通知 ---
|
|
|
-参数校验通过
|
|
|
-发送微信消息给 user_openid_123(appId: wx_app_001):内容 -您有一条新消息
|
|
|
-[微信][INFO]消息:发送成功
|
|
|
---- 发送流程结束 ---
|
|
|
---- 站内信通知 ---
|
|
|
-参数校验通过
|
|
|
-[企业办公平台] 站内信发送给 全体员工:内容 -下午 3 点会议室开会
|
|
|
-[站内信][INFO]消息:发送成功
|
|
|
---- 发送流程结束 ---
|
|
|
---- 空参数测试 ---
|
|
|
-参数校验失败:收件人或消息不能为空
|
|
|
-```
|
|
|
-
|
|
|
-### 思考题
|
|
|
-
|
|
|
-1. `SMSNotifier` 没有重写 `log()` 方法,调用时执行的是谁的 `log()`?
|
|
|
-2. 接口静态方法 `printNotificationType()` 能通过实现类对象调用吗?如 `sms.printNotificationType()` —— 尝试后说说你的发现。
|
|
|
-3. 如果 `Notifiable` 新增一个默认方法 `default void sendWithRetry(...)`,已有的 3 个实现类需要修改吗?这体现了接口默认方法的什么优势?
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 作业 2:模板方法设计模式——数据备份系统
|
|
|
-
|
|
|
-**难度**:⭐⭐⭐⭐
|
|
|
-**知识点**:模板方法设计模式、接口默认方法、抽象方法与默认方法的配合
|
|
|
-
|
|
|
-### 需求说明
|
|
|
-
|
|
|
-开发一个数据备份系统,所有备份任务都遵循相同的流程,但不同的备份方式(数据库备份、文件备份、云备份)在具体执行步骤上有所不同。
|
|
|
-
|
|
|
-**核心流程**(模板方法定义):
|
|
|
-1. **校验参数**(所有备份相同)—— 检查备份路径/配置是否合法
|
|
|
-2. **连接资源**(不同备份方式不同)← **抽象方法**
|
|
|
-3. **执行备份**(不同备份方式不同)← **抽象方法**
|
|
|
-4. **记录日志**(所有备份相同)—— 记录备份结果
|
|
|
-5. **关闭资源**(不同备份方式不同)← **抽象方法**
|
|
|
-
|
|
|
-### 使用接口 + 默认方法实现模板方法模式
|
|
|
-
|
|
|
-**`Backupable` 接口**:
|
|
|
-
|
|
|
-```java
|
|
|
-public interface Backupable {
|
|
|
- // ===== 抽象方法(实现类必须实现) =====
|
|
|
- void connect(); // 连接资源
|
|
|
- void executeBackup(); // 执行备份
|
|
|
- void close(); // 关闭资源
|
|
|
-
|
|
|
- // ===== 默认方法(可选重写) =====
|
|
|
- default boolean validateConfig(String path) {
|
|
|
- if (path != null && !path.isEmpty()) {
|
|
|
- System.out.println("配置校验通过:" + path);
|
|
|
- return true;
|
|
|
- }
|
|
|
- System.out.println("配置校验失败:路径不能为空");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- default void log(String operation, String status) {
|
|
|
- System.out.println("[备份日志] " + operation + " —— " + status);
|
|
|
- }
|
|
|
-
|
|
|
- // ===== 模板方法(定义固定流程,使用 default 关键字) =====
|
|
|
- default void backup(String configPath) {
|
|
|
- // 1. 校验配置
|
|
|
- if (!validateConfig(configPath)) return;
|
|
|
-
|
|
|
- // 2. 连接资源
|
|
|
- connect();
|
|
|
-
|
|
|
- // 3. 执行备份
|
|
|
- executeBackup();
|
|
|
-
|
|
|
- // 4. 记录日志
|
|
|
- log("数据备份", "完成");
|
|
|
-
|
|
|
- // 5. 关闭资源
|
|
|
- close();
|
|
|
-
|
|
|
- System.out.println("--- 备份流程结束 ---");
|
|
|
- }
|
|
|
-
|
|
|
- // ===== 静态方法 =====
|
|
|
- static void printSystemInfo() {
|
|
|
- System.out.println("数据备份系统 v2.0——支持数据库/文件/云备份");
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-### 要求实现以下 3 个备份类
|
|
|
-
|
|
|
-**`DatabaseBackup` 类**(实现 Backupable):
|
|
|
-- **私有属性**:`dbName`(数据库名,String)
|
|
|
-- **构造方法**:`DatabaseBackup(String dbName)`
|
|
|
-- **实现** `connect()` —— 输出 `"连接数据库:xxx"`
|
|
|
-- **实现** `executeBackup()` —— 输出 `"执行数据库备份:导出表结构和数据..."`
|
|
|
-- **实现** `close()` —— 输出 `"关闭数据库连接"`
|
|
|
-- **重写** `log()` —— 输出 `"[数据库备份] xxx —— xxx"`(覆盖默认日志格式)
|
|
|
-
|
|
|
-**`FileBackup` 类**(实现 Backupable):
|
|
|
-- **私有属性**:`sourceDir`(源目录,String)
|
|
|
-- **构造方法**:`FileBackup(String sourceDir)`
|
|
|
-- **实现** `connect()` —— 输出 `"扫描本地目录:xxx"`
|
|
|
-- **实现** `executeBackup()` —— 输出 `"执行文件备份:压缩文件并复制到备份目录..."`
|
|
|
-- **实现** `close()` —— 输出 `"释放文件流资源"`
|
|
|
-- **注意**:不重写 `log()`,使用接口默认实现
|
|
|
-
|
|
|
-**`CloudBackup` 类**(实现 Backupable):
|
|
|
-- **私有属性**:`cloudProvider`(云服务商,String,如 "阿里云OSS" / "腾讯云COS")
|
|
|
-- **构造方法**:`CloudBackup(String cloudProvider)`
|
|
|
-- **实现** `connect()` —— 输出 `"连接云存储服务:xxx"`
|
|
|
-- **实现** `executeBackup()` —— 输出 `"执行云备份:上传数据到云端..."`(上传完成后追加输出 `"上传完成!"`)
|
|
|
-- **实现** `close()` —— 输出 `"断开云存储连接"`
|
|
|
-- **重写** `validateConfig()` —— 在父类校验基础上,额外检查 `cloudProvider` 是否以 "云" 结尾,如果不是则输出 `"云服务商名称不合法"` 并返回 false
|
|
|
-
|
|
|
-### 测试类要求
|
|
|
-
|
|
|
-**`BackupTest` 类**:
|
|
|
-
|
|
|
-1. 调用接口静态方法输出系统信息
|
|
|
-2. 创建三种备份对象,分别调用 `backup()` 方法
|
|
|
-3. 测试配置校验失败的情况(传入空路径)
|
|
|
-
|
|
|
-```java
|
|
|
-public class BackupTest {
|
|
|
- public static void main(String[] args) {
|
|
|
- // 1. 输出系统信息
|
|
|
- Backupable.printSystemInfo();
|
|
|
-
|
|
|
- // 2. 数据库备份
|
|
|
- System.out.println("--- 数据库备份 ---");
|
|
|
- Backupable dbBackup = new DatabaseBackup("ERP_System");
|
|
|
- dbBackup.backup("/data/backup/db");
|
|
|
-
|
|
|
- // 3. 文件备份
|
|
|
- System.out.println("--- 文件备份 ---");
|
|
|
- Backupable fileBackup = new FileBackup("/home/user/documents");
|
|
|
- fileBackup.backup("/data/backup/files");
|
|
|
-
|
|
|
- // 4. 云备份
|
|
|
- System.out.println("--- 云备份 ---");
|
|
|
- Backupable cloudBackup = new CloudBackup("阿里云OSS");
|
|
|
- cloudBackup.backup("/data/backup/cloud");
|
|
|
-
|
|
|
- // 5. 测试校验失败
|
|
|
- System.out.println("--- 配置校验失败测试 ---");
|
|
|
- dbBackup.backup("");
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-### 预期输出
|
|
|
-
|
|
|
-```
|
|
|
-数据备份系统 v2.0——支持数据库/文件/云备份
|
|
|
---- 数据库备份 ---
|
|
|
-配置校验通过:/data/backup/db
|
|
|
-连接数据库:ERP_System
|
|
|
-执行数据库备份:导出表结构和数据...
|
|
|
-[数据库备份] 数据备份 —— 完成
|
|
|
-关闭数据库连接
|
|
|
---- 备份流程结束 ---
|
|
|
---- 文件备份 ---
|
|
|
-配置校验通过:/data/backup/files
|
|
|
-扫描本地目录:/home/user/documents
|
|
|
-执行文件备份:压缩文件并复制到备份目录...
|
|
|
-[备份日志] 数据备份 —— 完成
|
|
|
-释放文件流资源
|
|
|
---- 备份流程结束 ---
|
|
|
---- 云备份 ---
|
|
|
-配置校验通过:/data/backup/cloud
|
|
|
-连接云存储服务:阿里云OSS
|
|
|
-执行云备份:上传数据到云端...
|
|
|
-上传完成!
|
|
|
-[备份日志] 数据备份 —— 完成
|
|
|
-断开云存储连接
|
|
|
---- 备份流程结束 ---
|
|
|
---- 配置校验失败测试 ---
|
|
|
-配置校验失败:路径不能为空
|
|
|
-```
|
|
|
-
|
|
|
-### 对比思考
|
|
|
-
|
|
|
-| 对比维度 | 课堂 Notifiable + send() | 本作业 Backupable + backup() |
|
|
|
-|---------|------------------------|----------------------------|
|
|
|
-| 模板方法 | `send()` | `backup()` |
|
|
|
-| 抽象方法数量 | 1 个(`doSend`) | 3 个(`connect`、`executeBackup`、`close`) |
|
|
|
-| 默认方法数量 | 3 个(`validateParams`、`log`、`send`) | 3 个(`validateConfig`、`log`、`backup`) |
|
|
|
-| 静态方法 | `printNotificationType()` | `printSystemInfo()` |
|
|
|
-
|
|
|
-**思考题**:
|
|
|
-1. 模板方法 `backup()` 用 `default` 修饰而不是 `abstract`,为什么这样设计?
|
|
|
-2. 如果希望子类 **不能重写** 模板方法 `backup()`,应该用什么关键字修饰?(提示:回忆 abstract 中的 final 关键字)
|
|
|
-3. 观察 `DatabaseBackup` 重写了 `log()`,而 `FileBackup` 没有重写 —— 这体现了接口默认方法的什么优势?
|
|
|
-4. 本作业中 `Backupable` 是一个接口,如果改用抽象类实现,会有什么不同?(提示:单继承 vs 多实现)
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
## 📋 题目总览
|
|
|
|
|
|
| 序号 | 所属部分 | 核心知识点 | 难度 |
|
|
|
@@ -740,5 +455,3 @@ public class BackupTest {
|
|
|
| 练习 4 | 随堂 | 多态判断、多态与普通继承的区别 | ⭐⭐ |
|
|
|
| 进阶 1 | 进阶 | 多态数组 + instanceof + 向下转型 + 员工管理综合 | ⭐⭐⭐⭐ |
|
|
|
| 进阶 2 | 进阶 | 接口多态 + Usb 设备数组 + instanceof + 开闭原则 | ⭐⭐⭐⭐⭐ |
|
|
|
-| 作业 1 | 课后 | 接口默认方法/静态方法、通知系统扩展 | ⭐⭐⭐ |
|
|
|
-| 作业 2 | 课后 | 模板方法设计模式(接口默认方法实现)、数据备份系统 | ⭐⭐⭐⭐ |
|