课程内容:多态(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 ← 测试类
⚠ 注意:包名必须严格按题目要求书写(全小写),目录结构必须与包结构一致。
包名:p1_smarthome
知识点:接口定义与实现、接口多态(参数/数组)、向上转型、向下转型、instanceof 运算符、调用实现类特有方法、开闭原则
开发一个智能家居控制系统。不同家电的开关方式和运行逻辑不同,但都遵循统一的控制规范:打开 → 运行 → 关闭。此外,每种家电还有各自的特有功能(灯可以调色、空调可以定时、扫地机器人有不同清洁模式)。
请使用 接口多态 + instanceof + 向下转型 来设计,要求:
instanceof 判断设备类型后向下转型,调用特有功能| 方法 | 说明 |
|---|---|
void turnOn() |
打开设备 |
void doWork() |
执行工作 |
void turnOff() |
关闭设备 |
| 成员 | 说明 |
|---|---|
| 私有属性 | location(位置,String,如 "客厅" / "卧室") |
| 构造方法 | Light(String location) |
实现 turnOn() |
输出 "xxx 的灯已打开" |
实现 doWork() |
输出 "xxx 的灯照明中..." |
实现 turnOff() |
输出 "xxx 的灯已关闭" |
| 特有方法 | void changeColor(String color) —— 输出 "xxx 的灯光变为 xxx 色" |
| 成员 | 说明 |
|---|---|
| 私有属性 | roomName(房间名,String)、temperature(温度,int) |
| 构造方法 | AirConditioner(String roomName, int temperature) |
实现 turnOn() |
输出 "xxx 空调已打开,设定温度:xxx℃" |
实现 doWork() |
输出 "xxx 空调制冷中,当前室温正降至 xxx℃..." |
实现 turnOff() |
输出 "xxx 空调已关闭" |
| 特有方法 | void setTimer(int hours) —— 输出 "xxx 空调已设置 xxx 小时后自动关闭" |
| 成员 | 说明 |
|---|---|
| 私有属性 | 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 方法):
创建设备数组:创建一个 SmartDevice[] 数组,包含至少 5 个设备
SmartDevice light1 = new Light("客厅");
SmartDevice ac = new AirConditioner("主卧", 26);
SmartDevice robot = new RobotVacuum("石头T10", 80);
SmartDevice light2 = new Light("书房");
SmartDevice ac2 = new AirConditioner("次卧", 24);
遍历数组,执行通用工作流程:对每个设备依次调用 turnOn() → doWork() → turnOff()
遍历数组,执行特有功能:使用 instanceof 判断设备类型后向下转型,调用特有方法:
Light → 向下转型 → 调用 changeColor("暖黄")AirConditioner → 向下转型 → 调用 setTimer(4)(4 小时)RobotVacuum → 向下转型 → 调用 startCleaningMode("强力")编写静态方法 startDevice(SmartDevice device, int seconds):
device.turnOn()"设备工作中,请等待" + seconds + "秒..."device.doWork()instanceof 判断类型后向下转型,调用特有方法(与第 3 步逻辑相同)device.turnOff()main 中传入不同类型设备调用此方法SmartDevice device 能传入哪些类型的对象?调用 device.changeColor() 行吗?为什么?SmartSpeaker(智能音箱)实现 SmartDevice,SmartHomeController 需要修改哪些代码?是否符合"开闭原则"?SmartDevice 改为抽象类而不是接口,设计上会有什么不同?还能实现"一个类实现多个规范"吗?========== 智能家居启动 ==========
客厅的灯已打开
客厅的灯照明中...
客厅的灯已关闭
客厅的灯光变为暖黄色
主卧空调已打开,设定温度:26℃
主卧空调制冷中,当前室温正降至 26℃...
主卧空调已关闭
主卧空调已设置 4 小时后自动关闭
扫地机器人 石头T10 已启动,当前电量:80%
扫地机器人 石头T10 开始清扫地面...
扫地机器人 石头T10 已完成清扫,自动关闭
扫地机器人 石头T10 已切换到 强力 模式开始清扫
书房的灯已打开
书房的灯照明中...
书房的灯已关闭
书房的灯光变为暖黄色
次卧空调已打开,设定温度:24℃
次卧空调制冷中,当前室温正降至 24℃...
次卧空调已关闭
次卧空调已设置 4 小时后自动关闭
========== 测试 startDevice 方法 ==========
客厅的灯已打开
设备工作中,请等待10秒...
客厅的灯照明中...
客厅的灯光变为暖黄色
客厅的灯已关闭
提示:
- 在
main中调用startDevice时,可以直接传入数组中的元素,如controller.startDevice(devices[0], 10);instanceof判断的是运行时类型,不是编译时类型- 特有方法只能通过向下转型后的子类引用调用,父类/接口引用无法直接调用
开发一个智能家居控制系统。不同家电的开关方式和运行逻辑不同,但都遵循统一的控制规范:打开 → 运行 → 关闭。
请使用 接口多态 来设计,要求控制器不依赖具体家电类型。
| 方法 | 说明 |
|---|---|
void turnOn() |
打开设备 |
void doWork() |
执行工作 |
void turnOff() |
关闭设备 |
| 成员 | 说明 |
|---|---|
| 私有属性 | location(位置,String,如 "客厅" / "卧室") |
| 构造方法 | Light(String location) |
实现 turnOn() |
输出 "xxx 的灯已打开" |
实现 doWork() |
输出 "xxx 的灯照明中..." |
实现 turnOff() |
输出 "xxx 的灯已关闭" |
| 成员 | 说明 |
|---|---|
| 私有属性 | roomName(房间名,String)、temperature(温度,int) |
| 构造方法 | AirConditioner(String roomName, int temperature) |
实现 turnOn() |
输出 "xxx 空调已打开,设定温度:xxx℃" |
实现 doWork() |
输出 "xxx 空调制冷中,当前室温正降至 xxx℃..." |
实现 turnOff() |
输出 "xxx 空调已关闭" |
| 成员 | 说明 |
|---|---|
| 私有属性 | model(型号,String)、batteryLevel(电量,int,百分比) |
| 构造方法 | RobotVacuum(String model, int batteryLevel) |
实现 turnOn() |
输出 "扫地机器人 xxx 已启动,当前电量:xxx%" |
实现 doWork() |
输出 "扫地机器人 xxx 开始清扫地面..." |
实现 turnOff() |
输出 "扫地机器人 xxx 已完成清扫,自动关闭" |
SmartHomeController 类:
创建设备数组:SmartDevice[] 数组,包含至少 4 个设备
SmartDevice light1 = new Light("客厅");
SmartDevice ac = new AirConditioner("主卧", 26);
SmartDevice robot = new RobotVacuum("石头T10", 80);
SmartDevice light2 = new Light("书房");
遍历执行工作流程:对每个设备依次调用 turnOn() → doWork() → turnOff()
编写静态方法 startDevice(SmartDevice device, int seconds):
device.turnOn()"设备工作中,请等待" + seconds + "秒..."device.doWork()device.turnOff()main 中用不同类型设备调用该方法SmartDevice device 可以传入哪些类型的对象?这体现了多态的什么好处?SmartSpeaker 实现 SmartDevice,控制器需要修改吗?NoteBook.useUsb(Usb usb) 案例,设计思路有什么相似之处?========== 智能家居启动 ==========
客厅的灯已打开
客厅的灯照明中...
客厅的灯已关闭
主卧空调已打开,设定温度:26℃
主卧空调制冷中,当前室温正降至 26℃...
主卧空调已关闭
扫地机器人 石头T10 已启动,当前电量:80%
扫地机器人 石头T10 开始清扫地面...
扫地机器人 石头T10 已完成清扫,自动关闭
书房的灯已打开
书房的灯照明中...
书房的灯已关闭
========== 测试 startDevice 方法 ==========
客厅的灯已打开
设备工作中,请等待10秒...
客厅的灯照明中...
客厅的灯已关闭
包名:p2_myarray
知识点:构造方法、数组扩容、方法封装、增删改查、元素前移、冒泡排序
模仿
ArrayList的底层原理,自己动手实现一个可变数组(动态数组)。
Java 中普通数组 int[] 一旦创建,长度就固定了,无法动态增减元素。请自行实现一个 可变整型数组 MyArray 类,内部使用 int[] 存储数据,对外提供便捷的增删改查方法。
私有属性:
| 属性 | 类型 | 说明 |
|---|---|---|
data |
int[] |
内部真正存储数据的数组 |
size |
int |
当前已存储的元素个数(不是数组长度) |
构造方法:
| 构造方法 | 说明 |
|---|---|
MyArray() |
无参构造,默认初始化容量为 10 的数组 |
MyArray(int capacity) |
指定初始容量,如果传入的容量 ≤ 0,则使用默认容量 10 |
add(int element)data.length * 3 / 2)data 引用data[size],size 自增 1get(int index)"下标越界,无法获取元素",返回 -1data[index]set(int index, int element)"下标越界,无法修改元素"data[index] 赋值为 elementsize()size 属性值capacity()data.lengthisEmpty()size == 0 返回 true,否则 falseindexOf(int element)-1contains(int element)indexOf(),根据返回值是否为 -1 判断remove(int index)"下标越界,无法删除元素",返回 -1size-- → 返回被删除的值removeByValue(int element)indexOf() 获取下标-1 则输出 "未找到该元素,删除失败",返回 falseremove(index),返回 truesort(boolean ascending)true 升序,false 降序data[0] ~ data[size-1] 排序print()[元素1, 元素2, 元素3, ...][]clear()size 置为 0(内部数组保留)MyArrayTest 类:
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
}
}
add() 方法中每次只扩容 1 个容量(data.length + 1),当添加大量元素时性能会怎样?为什么选择 1.5 倍?remove() 方法中为什么要将后面的元素向前移动?如果删除的是最后一个元素还需要移动吗?MyArray 改造为支持任意类型的 MyArrayList<T>?包名:p3_exam
知识点:抽象类、抽象方法、模板方法(final)、方法重写、继承
模板方法模式(Template Method Pattern)是抽象类的经典应用场景。父类定义算法的「骨架」,子类实现特定的「步骤」。
模拟一个「在线考试系统」,每门考试都需要经过以下步骤:
ExamTemplate 抽象类(父类):
login() —— 输出 "考生登录系统,验证身份... 登录成功!"deliverPaper() —— 输出 "发放试卷,考试开始!"void conductExam() —— 考试进行中(不同科目不同)void gradeExam() —— 收卷评分(不同科目不同)announceResult() —— 输出 "成绩公布!请登录系统查看"final void startExam() —— 按顺序执行完整考试流程:
login()deliverPaper()conductExam()gradeExam()announceResult()JavaExam 类(继承 ExamTemplate):
conductExam() —— 输出 "Java 考试进行中:编写代码,回答选择题..."gradeExam() —— 输出 "Java 考试评分:检查代码正确性,选择题自动判分..."MathExam 类(继承 ExamTemplate):
conductExam() —— 输出 "数学考试进行中:解答微积分、线性代数题目..."gradeExam() —— 输出 "数学考试评分:答案比对,步骤给分..."ExamTest 类:
JavaExam 对象,调用 startExam() 方法MathExam 对象,调用 startExam() 方法========== Java 考试 ==========
考生登录系统,验证身份... 登录成功!
发放试卷,考试开始!
Java 考试进行中:编写代码,回答选择题...
Java 考试评分:检查代码正确性,选择题自动判分...
成绩公布!请登录系统查看
========== 数学考试 ==========
考生登录系统,验证身份... 登录成功!
发放试卷,考试开始!
数学考试进行中:解答微积分、线性代数题目...
数学考试评分:答案比对,步骤给分...
成绩公布!请登录系统查看
startExam() 方法为什么用 final 修饰?如果不加 final 会怎样?| 题号 | 名称 | 核心知识点 | 难度 |
|---|---|---|---|
| 第 1 题 | 智能家居控制系统 | 接口多态、向上转型/向下转型、instanceof、调用实现类特有方法、开闭原则 | ⭐⭐⭐ |
| 第 2 题 | MyArray 可变数组 | 构造方法、数组扩容、增删改查、元素前移、冒泡排序 | ⭐⭐⭐⭐⭐ |
| 第 3 题 | 在线考试系统(模板方法) | 抽象类、模板方法、final、方法重写 | ⭐⭐⭐⭐⭐ |