# 2026-07-24 JavaSE 课后作业 > **课程内容**:多态(Polymorphism)、多态中的转型(向上转型/向下转型)、instanceof 运算符、抽象类与多态结合、接口多态案例(USB + 笔记本)、内部类(成员内部类/静态成员内部类/局部内部类) > > **涉及前序知识**:继承、抽象类、接口、封装、构造方法、方法重写 > > **提交方式**:在 `提交\姓名\` 目录下创建对应的 Java 源文件 > > **截止时间**:下次上课前 --- ## 📌 本次作业重要说明 ### 命名要求 | 项目 | 要求 | |------|------| | **包名** | **必须按题目指定的包名创建**(全小写) | | **类名** | 自由命名,但需**见名知意** | | **测试类** | 建议命名为 `TestXxx` 或 `XxxTest` | | **文件名** | 必须与类名一致 | ### 提交目录结构示例 ``` 提交\ └─ 张三\ ├─ p1_smarthome\ ← 第 1 题的包 │ ├─ SmartDevice.java ← 接口 │ ├─ Light.java ← 实现类(特有:调色) │ ├─ AirConditioner.java ← 实现类(特有:定时) │ ├─ RobotVacuum.java ← 实现类(特有:清洁模式) │ └─ SmartHomeController.java ← 测试类(多态 + instanceof) ├─ p2_myarray\ ← 第 2 题的包 │ └─ MyArray.java + MyArrayTest.java └─ p3_exam\ ← 第 3 题的包 ├─ ExamTemplate.java ← 抽象类(模板方法) ├─ JavaExam.java ← 子类 ├─ MathExam.java ← 子类 └─ ExamTest.java ← 测试类 ``` > ⚠ **注意**:包名必须严格按题目要求书写(全小写),目录结构必须与包结构一致。 --- ## 第 1 题:接口多态 + 转型 + instanceof——智能家居控制系统 ⭐⭐⭐ **包名**:`p1_smarthome` **知识点**:接口定义与实现、接口多态(参数/数组)、向上转型、向下转型、instanceof 运算符、调用实现类特有方法、开闭原则 ### 需求说明 开发一个**智能家居控制系统**。不同家电的开关方式和运行逻辑不同,但都遵循统一的控制规范:**打开 → 运行 → 关闭**。此外,每种家电还有**各自的特有功能**(灯可以调色、空调可以定时、扫地机器人有不同清洁模式)。 请使用 **接口多态 + instanceof + 向下转型** 来设计,要求: - 控制器通过接口类型操作所有设备(多态) - 通过 `instanceof` 判断设备类型后向下转型,调用特有功能 ### 接口设计 #### SmartDevice 接口 | 方法 | 说明 | |------|------| | `void turnOn()` | 打开设备 | | `void doWork()` | 执行工作 | | `void turnOff()` | 关闭设备 | ### 实现类要求 #### Light 类(灯——实现 SmartDevice) | 成员 | 说明 | |------|------| | 私有属性 | `location`(位置,String,如 "客厅" / "卧室") | | 构造方法 | `Light(String location)` | | 实现 `turnOn()` | 输出 `"xxx 的灯已打开"` | | 实现 `doWork()` | 输出 `"xxx 的灯照明中..."` | | 实现 `turnOff()` | 输出 `"xxx 的灯已关闭"` | | **特有方法** | `void changeColor(String color)` —— 输出 `"xxx 的灯光变为 xxx 色"` | #### AirConditioner 类(空调——实现 SmartDevice) | 成员 | 说明 | |------|------| | 私有属性 | `roomName`(房间名,String)、`temperature`(温度,int) | | 构造方法 | `AirConditioner(String roomName, int temperature)` | | 实现 `turnOn()` | 输出 `"xxx 空调已打开,设定温度:xxx℃"` | | 实现 `doWork()` | 输出 `"xxx 空调制冷中,当前室温正降至 xxx℃..."` | | 实现 `turnOff()` | 输出 `"xxx 空调已关闭"` | | **特有方法** | `void setTimer(int hours)` —— 输出 `"xxx 空调已设置 xxx 小时后自动关闭"` | #### RobotVacuum 类(扫地机器人——实现 SmartDevice) | 成员 | 说明 | |------|------| | 私有属性 | `model`(型号,String)、`batteryLevel`(电量,int,百分比) | | 构造方法 | `RobotVacuum(String model, int batteryLevel)` | | 实现 `turnOn()` | 输出 `"扫地机器人 xxx 已启动,当前电量:xxx%"` | | 实现 `doWork()` | 输出 `"扫地机器人 xxx 开始清扫地面..."` | | 实现 `turnOff()` | 输出 `"扫地机器人 xxx 已完成清扫,自动关闭"` | | **特有方法** | `void startCleaningMode(String mode)` —— 输出 `"扫地机器人 xxx 已切换到 xxx 模式开始清扫"`(mode 如 "强力" / "静音" / "边缘") | ### 测试类要求 **`SmartHomeController` 类**(测试类,含 main 方法): 1. **创建设备数组**:创建一个 `SmartDevice[]` 数组,包含至少 5 个设备 ```java SmartDevice light1 = new Light("客厅"); SmartDevice ac = new AirConditioner("主卧", 26); SmartDevice robot = new RobotVacuum("石头T10", 80); SmartDevice light2 = new Light("书房"); SmartDevice ac2 = new AirConditioner("次卧", 24); ``` 2. **遍历数组,执行通用工作流程**:对每个设备依次调用 `turnOn()` → `doWork()` → `turnOff()` 3. **遍历数组,执行特有功能**:使用 `instanceof` 判断设备类型后向下转型,调用特有方法: - 如果是 `Light` → 向下转型 → 调用 `changeColor("暖黄")` - 如果是 `AirConditioner` → 向下转型 → 调用 `setTimer(4)`(4 小时) - 如果是 `RobotVacuum` → 向下转型 → 调用 `startCleaningMode("强力")` 4. **编写静态方法 `startDevice(SmartDevice device, int seconds)`**: - 调用 `device.turnOn()` - 输出 `"设备工作中,请等待" + seconds + "秒..."` - 调用 `device.doWork()` - 使用 `instanceof` 判断类型后向下转型,调用特有方法(与第 3 步逻辑相同) - 调用 `device.turnOff()` - 在 `main` 中传入不同类型设备调用此方法 ### 思考题 1. 方法参数 `SmartDevice device` 能传入哪些类型的对象?调用 `device.changeColor()` 行吗?为什么? 2. 如果新增一个 `SmartSpeaker`(智能音箱)实现 `SmartDevice`,`SmartHomeController` 需要修改哪些代码?是否符合"开闭原则"? 3. 如果将 `SmartDevice` 改为抽象类而不是接口,设计上会有什么不同?还能实现"一个类实现多个规范"吗? ### 预期输出 ``` ========== 智能家居启动 ========== 客厅的灯已打开 客厅的灯照明中... 客厅的灯已关闭 客厅的灯光变为暖黄色 主卧空调已打开,设定温度:26℃ 主卧空调制冷中,当前室温正降至 26℃... 主卧空调已关闭 主卧空调已设置 4 小时后自动关闭 扫地机器人 石头T10 已启动,当前电量:80% 扫地机器人 石头T10 开始清扫地面... 扫地机器人 石头T10 已完成清扫,自动关闭 扫地机器人 石头T10 已切换到 强力 模式开始清扫 书房的灯已打开 书房的灯照明中... 书房的灯已关闭 书房的灯光变为暖黄色 次卧空调已打开,设定温度:24℃ 次卧空调制冷中,当前室温正降至 24℃... 次卧空调已关闭 次卧空调已设置 4 小时后自动关闭 ========== 测试 startDevice 方法 ========== 客厅的灯已打开 设备工作中,请等待10秒... 客厅的灯照明中... 客厅的灯光变为暖黄色 客厅的灯已关闭 ``` > **提示**: > - 在 `main` 中调用 `startDevice` 时,可以直接传入数组中的元素,如 `controller.startDevice(devices[0], 10);` > - `instanceof` 判断的是**运行时类型**,不是编译时类型 > - 特有方法只能通过**向下转型**后的子类引用调用,父类/接口引用无法直接调用 ### 需求说明 开发一个**智能家居控制系统**。不同家电的开关方式和运行逻辑不同,但都遵循统一的控制规范:**打开 → 运行 → 关闭**。 请使用 **接口多态** 来设计,要求控制器不依赖具体家电类型。 ### 类设计 #### SmartDevice 接口 | 方法 | 说明 | |------|------| | `void turnOn()` | 打开设备 | | `void doWork()` | 执行工作 | | `void turnOff()` | 关闭设备 | #### Light 类(灯——实现 SmartDevice) | 成员 | 说明 | |------|------| | 私有属性 | `location`(位置,String,如 "客厅" / "卧室") | | 构造方法 | `Light(String location)` | | 实现 `turnOn()` | 输出 `"xxx 的灯已打开"` | | 实现 `doWork()` | 输出 `"xxx 的灯照明中..."` | | 实现 `turnOff()` | 输出 `"xxx 的灯已关闭"` | #### AirConditioner 类(空调——实现 SmartDevice) | 成员 | 说明 | |------|------| | 私有属性 | `roomName`(房间名,String)、`temperature`(温度,int) | | 构造方法 | `AirConditioner(String roomName, int temperature)` | | 实现 `turnOn()` | 输出 `"xxx 空调已打开,设定温度:xxx℃"` | | 实现 `doWork()` | 输出 `"xxx 空调制冷中,当前室温正降至 xxx℃..."` | | 实现 `turnOff()` | 输出 `"xxx 空调已关闭"` | #### RobotVacuum 类(扫地机器人——实现 SmartDevice) | 成员 | 说明 | |------|------| | 私有属性 | `model`(型号,String)、`batteryLevel`(电量,int,百分比) | | 构造方法 | `RobotVacuum(String model, int batteryLevel)` | | 实现 `turnOn()` | 输出 `"扫地机器人 xxx 已启动,当前电量:xxx%"` | | 实现 `doWork()` | 输出 `"扫地机器人 xxx 开始清扫地面..."` | | 实现 `turnOff()` | 输出 `"扫地机器人 xxx 已完成清扫,自动关闭"` | ### 测试类要求 **`SmartHomeController` 类**: 1. **创建设备数组**:`SmartDevice[]` 数组,包含至少 4 个设备 ```java SmartDevice light1 = new Light("客厅"); SmartDevice ac = new AirConditioner("主卧", 26); SmartDevice robot = new RobotVacuum("石头T10", 80); SmartDevice light2 = new Light("书房"); ``` 2. **遍历执行工作流程**:对每个设备依次调用 `turnOn()` → `doWork()` → `turnOff()` 3. **编写静态方法** `startDevice(SmartDevice device, int seconds)`: - 调用 `device.turnOn()` - 输出 `"设备工作中,请等待" + seconds + "秒..."` - 调用 `device.doWork()` - 调用 `device.turnOff()` - 在 `main` 中用不同类型设备调用该方法 ### 思考题 1. 方法参数 `SmartDevice device` 可以传入哪些类型的对象?这体现了多态的什么好处? 2. 如果新增 `SmartSpeaker` 实现 `SmartDevice`,控制器需要修改吗? 3. 对比课堂 `NoteBook.useUsb(Usb usb)` 案例,设计思路有什么相似之处? ### 预期输出 ``` ========== 智能家居启动 ========== 客厅的灯已打开 客厅的灯照明中... 客厅的灯已关闭 主卧空调已打开,设定温度:26℃ 主卧空调制冷中,当前室温正降至 26℃... 主卧空调已关闭 扫地机器人 石头T10 已启动,当前电量:80% 扫地机器人 石头T10 开始清扫地面... 扫地机器人 石头T10 已完成清扫,自动关闭 书房的灯已打开 书房的灯照明中... 书房的灯已关闭 ========== 测试 startDevice 方法 ========== 客厅的灯已打开 设备工作中,请等待10秒... 客厅的灯照明中... 客厅的灯已关闭 ``` --- ## 第 2 题:综合大挑战——MyArray 可变数组(动态数组底层实现)⭐⭐⭐⭐⭐ **包名**:`p2_myarray` **知识点**:构造方法、数组扩容、方法封装、增删改查、元素前移、冒泡排序 > 模仿 `ArrayList` 的底层原理,自己动手实现一个可变数组(动态数组)。 ### 需求背景 Java 中普通数组 `int[]` 一旦创建,长度就固定了,无法动态增减元素。请自行实现一个 **可变整型数组** `MyArray` 类,内部使用 `int[]` 存储数据,对外提供便捷的增删改查方法。 ### 实体类说明:MyArray **私有属性**: | 属性 | 类型 | 说明 | |------|------|------| | `data` | `int[]` | 内部真正存储数据的数组 | | `size` | `int` | 当前已存储的元素个数(不是数组长度) | **构造方法**: | 构造方法 | 说明 | |---------|------| | `MyArray()` | 无参构造,默认初始化容量为 **10** 的数组 | | `MyArray(int capacity)` | 指定初始容量,如果传入的容量 ≤ 0,则使用默认容量 10 | ### 核心方法列表(共 11 个) #### 1. 添加元素 — `add(int element)` - 如果内部数组已满,自动扩容为当前容量的 **1.5 倍**(`data.length * 3 / 2`) - 扩容步骤:创建新数组 → 复制旧元素 → 替换 `data` 引用 - 将新元素存入 `data[size]`,`size` 自增 1 #### 2. 获取元素 — `get(int index)` - 下标越界时输出 `"下标越界,无法获取元素"`,返回 `-1` - 否则返回 `data[index]` #### 3. 修改元素 — `set(int index, int element)` - 下标越界时输出 `"下标越界,无法修改元素"` - 否则将 `data[index]` 赋值为 `element` #### 4. 获取长度 — `size()` - 返回 `size` 属性值 #### 5. 获取容量 — `capacity()` - 返回 `data.length` #### 6. 判断是否为空 — `isEmpty()` - `size == 0` 返回 `true`,否则 `false` #### 7. 查找元素下标 — `indexOf(int element)` - 从前向后遍历,找到第一个匹配的元素下标,没找到返回 `-1` #### 8. 判断是否包含 — `contains(int element)` - 调用 `indexOf()`,根据返回值是否为 `-1` 判断 #### 9. 删除指定位置的元素 — `remove(int index)` - 下标越界时输出 `"下标越界,无法删除元素"`,返回 `-1` - 否则:保存被删除的值 → 后面元素前移一位 → `size--` → 返回被删除的值 #### 10. 删除指定值的元素 — `removeByValue(int element)` - 调用 `indexOf()` 获取下标 - 下标为 `-1` 则输出 `"未找到该元素,删除失败"`,返回 `false` - 否则调用 `remove(index)`,返回 `true` #### 11. 数组排序 — `sort(boolean ascending)` - `true` 升序,`false` 降序 - 使用**冒泡排序**,只对 `data[0] ~ data[size-1]` 排序 ### 辅助方法 #### `print()` - 遍历输出格式:`[元素1, 元素2, 元素3, ...]` - 空数组输出 `[]` #### `clear()` - 将 `size` 置为 `0`(内部数组保留) ### 测试类要求 **`MyArrayTest` 类**: ```java public class MyArrayTest { public static void main(String[] args) { // 1. 创建一个初始容量为 3 的 MyArray 对象 MyArray arr = new MyArray(3); // 2. 输出初始状态 System.out.println("初始容量:" + arr.capacity()); // 3 System.out.println("是否为空:" + arr.isEmpty()); // true // 3. 添加 5 个元素(触发自动扩容) for (int i = 1; i <= 5; i++) { arr.add(i * 10); // 添加 10, 20, 30, 40, 50 } arr.print(); // [10, 20, 30, 40, 50] // 4. 测试各个方法 System.out.println("元素个数:" + arr.size()); // 5 System.out.println("当前容量:" + arr.capacity()); // 6 System.out.println("下标2的元素:" + arr.get(2)); // 30 System.out.println("是否包含40:" + arr.contains(40)); // true System.out.println("50的下标:" + arr.indexOf(50)); // 4 // 5. 测试修改 arr.set(1, 99); arr.print(); // [10, 99, 30, 40, 50] // 6. 测试删除 int deleted = arr.remove(2); System.out.println("被删除的元素:" + deleted); // 30 arr.print(); // [10, 99, 40, 50] // 7. 测试按值删除 arr.removeByValue(10); arr.print(); // [99, 40, 50] // 8. 测试升序排序 arr.sort(true); arr.print(); // [40, 50, 99] // 9. 测试降序排序 arr.sort(false); arr.print(); // [99, 50, 40] // 10. 测试越界访问 System.out.println(arr.get(10)); // 下标越界,无法获取元素 → -1 arr.remove(10); // 下标越界,无法删除元素 → -1 } } ``` ### 思考题 1. 如果 `add()` 方法中每次只扩容 1 个容量(`data.length + 1`),当添加大量元素时性能会怎样?为什么选择 1.5 倍? 2. `remove()` 方法中为什么要将后面的元素向前移动?如果删除的是最后一个元素还需要移动吗? 3. 如何将 `MyArray` 改造为支持任意类型的 `MyArrayList`? --- ## 第 3 题:模板方法设计模式——在线考试系统 ⭐⭐⭐⭐⭐ **包名**:`p3_exam` **知识点**:抽象类、抽象方法、模板方法(`final`)、方法重写、继承 > 模板方法模式(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 题 | 智能家居控制系统 | 接口多态、向上转型/向下转型、instanceof、调用实现类特有方法、开闭原则 | ⭐⭐⭐ | | 第 2 题 | MyArray 可变数组 | 构造方法、数组扩容、增删改查、元素前移、冒泡排序 | ⭐⭐⭐⭐⭐ | | 第 3 题 | 在线考试系统(模板方法) | 抽象类、模板方法、final、方法重写 | ⭐⭐⭐⭐⭐ |