说明:以下习题围绕今日授课核心内容——抽象类(abstract) 和 接口(Interface) 展开,涵盖抽象类的定义、特点、抽象方法的重写、抽象类与对象数组/排序的综合应用,以及接口的定义、实现、多实现等入门知识。
题目分为四部分:随堂练习(抽象类入门,4 题)、进阶练习(综合挑战,2 题)、课外拓展(1 题)和 接口入门练习(3 题)。
以下练习围绕抽象类的基础概念和用法,由浅入深。
难度:⭐
知识点:抽象类的定义、抽象方法、子类重写抽象方法
创建一个乐器抽象体系:
Instrument 抽象类(父类):
play() —— 演奏乐器(无返回值、无参数)Piano 类(继承 Instrument):
play() —— 输出 "弹钢琴:叮咚叮咚~"Violin 类(继承 Instrument):
play() —— 输出 "拉小提琴:咿呀咿呀~"Drum 类(继承 Instrument):
play() —— 输出 "打鼓:咚咚咚!"测试类要求:
Piano、Violin、Drum 各一个对象play() 方法play() 在不同的子类中有不同的行为——这就是 抽象方法多态 的体现预期输出:
弹钢琴:叮咚叮咚~
拉小提琴:咿呀咿呀~
打鼓:咚咚咚!
难度:⭐⭐
知识点:抽象类定义、抽象方法(计算周长 + 计算面积)、子类重写所有抽象方法
创建一个图形抽象体系,要求抽象父类中定义 计算周长 和 计算面积 的抽象方法。
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 × π × rRectangle 类(继承 Shape):
width(宽度,double)、height(高度,double)Rectangle(String color, double width, double height)getArea() —— 返回 width × heightgetPerimeter() —— 返回 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测试类要求:
Circle 对象("红色", 5.0),调用 showInfo() 输出信息Rectangle 对象("蓝色", 4.0, 6.0),调用 showInfo() 输出信息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)保留两位小数
难度:⭐⭐
知识点:抽象类、抽象方法重写、构造方法 + 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 + bonusProgrammer 类(继承 Employee):
projectBonus(项目奖金,double)Programmer(String name, double baseSalary, double projectBonus)work() —— 输出 "程序员在编写代码"calculateSalary() —— 返回 baseSalary + projectBonus测试类要求:
Manager 对象("张经理", 15000, 5000),调用 work() 和 showInfo()Programmer 对象("李编程", 12000, 3000),调用 work() 和 showInfo()Employee 改为非抽象类,把 work() 和 calculateSalary() 改为普通方法,会有什么问题?预期输出:
经理在管理团队
姓名:张经理,薪资:20000.0
程序员在编写代码
姓名:李编程,薪资:15000.0
难度:⭐⭐
知识点:抽象类设计思想、何时使用抽象类
判断以下场景中,哪些类 应该 定义为抽象类,哪些 不应该,并说明理由。
| 序号 | 类名 | 描述 | 是否抽象? | 理由 |
|---|---|---|---|---|
| 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 | ✅ 应该 | 不同数据库的连接和查询方式不同,适合用抽象类定义规范 |
以下为综合挑战题,融合抽象类与对象数组、排序、多态等知识点。
综合运用抽象类、对象数组、冒泡排序、多态等知识点。
利用 练习 2 中定义的 Shape 抽象类及其子类(Circle、Rectangle、Triangle),创建一个图形数组,按 面积从大到小 排序。
直接复用练习 2 中的 Shape(抽象类)、Circle、Rectangle、Triangle 类。
ShapeSortTest 类:
Shape[] 数组,包含至少 5 个不同的图形对象示例数据:
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
遍历输出所有图形信息:调用每个元素的 showInfo() 方法
按面积从大到小排序:使用冒泡排序,按 getArea() 返回值降序排列
排序后再次输出所有图形信息
查找面积最大的图形:遍历数组,输出面积最大的图形信息
Shape[],为什么能存储 Circle、Rectangle、Triangle 等不同类型的对象?这体现了 OOP 的哪个特征?Square(正方形)类,它应该继承谁?需要重写哪些方法?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)
开发一个支付系统,支持多种支付方式。每种支付方式的 支付逻辑不同,但都有 支付 和 退款 两个核心行为。
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 类:
Payment[] 数组,包含至少 4 笔不同的支付记录示例数据:
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");
遍历执行支付:遍历数组,对每个元素调用 pay() 和 showPaymentInfo() 方法
遍历执行退款:遍历数组,对每个元素调用 refund() 方法
统计总支付金额:遍历数组,累加每个元素的 amount,输出总金额
ApplePay 类,需要做哪些工作?pay() 和 refund() 不是抽象方法,而是在父类中写一个默认实现,会有什么弊端?========== 执行支付 ==========
微信支付:扫描二维码,扣款 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()在父类中已经声明为普通方法(非抽象),子类可以选择重写也可以不重写。这里要求 重写 以便输出不同的支付方式名称- 抽象类的意义在于:定义统一规范,强制子类实现特定行为
说明:模板方法模式(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 会怎样?以下练习围绕 接口(Interface) 的基础概念和用法,由浅入深。
难度:⭐
知识点:接口定义、接口方法、implements 实现接口
创建一个播放器接口体系:
Playable 接口:
void play() —— 播放void pause() —— 暂停void stop() —— 停止MP3Player 类(实现 Playable):
songName(歌曲名,String)MP3Player(String songName)play() —— 输出 "正在播放:xxx"pause() —— 输出 "暂停播放:xxx"stop() —— 输出 "停止播放:xxx"VideoPlayer 类(实现 Playable):
movieName(电影名,String)VideoPlayer(String movieName)play() —— 输出 "正在播放视频:xxx"pause() —— 输出 "暂停播放视频:xxx"stop() —— 输出 "停止播放视频:xxx"测试类要求:
MP3Player 对象("青花瓷"),依次调用 play()、pause()、stop()VideoPlayer 对象("哪吒"),依次调用 play()、pause()、stop()Playable 接口中三个方法的默认修饰符是什么?预期输出:
正在播放:青花瓷
暂停播放:青花瓷
停止播放:青花瓷
正在播放视频:哪吒
暂停播放视频:哪吒
停止播放视频:哪吒
难度:⭐⭐
知识点:接口定义、implements 实现、类继承父类的同时实现接口
参照课堂上的 USB 接口标准示例,创建一个充电器接口体系:
Charger 接口:
void connect() —— 连接设备void charging() —— 正在充电void disconnect() —— 断开连接Device 类(父类):
brand(品牌,String)、model(型号,String)Device(String brand, String model)showInfo() —— 输出 "设备:xxx 品牌:xxx"Phone 类(继承 Device 并实现 Charger):
batteryCapacity(电池容量,int,单位 mAh)Phone(String brand, String model, int batteryCapacity)connect() —— 输出 "手机已连接充电器"charging() —— 输出 "手机正在充电,电池容量:xxx mAh"disconnect() —— 输出 "手机已断开充电器"Laptop 类(继承 Device 并实现 Charger):
power(功率,int,单位 W)Laptop(String brand, String model, int power)connect() —— 输出 "笔记本电脑已连接电源"charging() —— 输出 "笔记本电脑正在充电,功率:xxx W"disconnect() —— 输出 "笔记本电脑已断开电源"测试类要求:
Phone 对象("华为", "Mate60", 5000),调用 showInfo(),然后依次调用充电三步骤Laptop 对象("联想", "ThinkPad", 65),调用 showInfo(),然后依次调用充电三步骤预期输出:
设备:Mate60 品牌:华为
手机已连接充电器
手机正在充电,电池容量:5000 mAh
手机已断开充电器
设备:ThinkPad 品牌:联想
笔记本电脑已连接电源
笔记本电脑正在充电,功率:65 W
笔记本电脑已断开电源
难度:⭐⭐⭐
知识点:一个类实现多个接口、接口的扩展性
创建一个多功能设备,同时实现 拍照 和 播放 两个接口。
Camera 接口:
void takePhoto() —— 拍照void recordVideo() —— 录像Playable 接口(复用练习 5 的定义):
void play() —— 播放void pause() —— 暂停void stop() —— 停止SmartPhone 类(实现 Camera 和 Playable 两个接口):
brand(品牌,String)、model(型号,String)SmartPhone(String brand, String model)takePhoto() —— 输出 "xxx xxx 拍照:咔嚓!"recordVideo() —— 输出 "xxx xxx 录像:开始录制视频..."play() —— 输出 "xxx xxx 播放音乐"pause() —— 输出 "xxx xxx 暂停播放"stop() —— 输出 "xxx xxx 停止播放"注意:
xxx xxx部分使用品牌和型号拼接,如"苹果 iPhone15"。
测试类要求:
SmartPhone 对象("苹果", "iPhone15")takePhoto()、recordVideo()play()、pause()、stop()思考题:
预期输出:
苹果 iPhone15 拍照:咔嚓!
苹果 iPhone15 录像:开始录制视频...
苹果 iPhone15 播放音乐
苹果 iPhone15 暂停播放
苹果 iPhone15 停止播放
| 序号 | 所属部分 | 核心知识点 | 难度 |
|---|---|---|---|
| 练习 1 | 随堂 | 抽象类定义、抽象方法、子类重写抽象方法 | ⭐ |
| 练习 2 | 随堂 | 抽象类:计算周长 + 计算面积、方法重写、super() 调用父类构造方法 | ⭐⭐ |
| 练习 3 | 随堂 | 抽象类 + 构造方法 + super()、抽象方法重写 | ⭐⭐ |
| 练习 4 | 随堂 | 抽象类设计思想、判断何时使用抽象类 | ⭐⭐ |
| 进阶 1 | 进阶 | 抽象类 + 对象数组 + 冒泡排序 + 多态 | ⭐⭐⭐⭐ |
| 进阶 2 | 进阶 | 抽象类 + 多态 + 继承 + 综合应用 | ⭐⭐⭐⭐⭐ |
| 拓展 3 | 课外 | 模板方法设计模式(抽象类经典应用) | ⭐⭐⭐⭐⭐ |
| 练习 5 | 接口入门 | 接口定义、接口方法、implements 实现接口 | ⭐ |
| 练习 6 | 接口入门 | 接口实现、类继承父类同时实现接口(Charger 充电规范) | ⭐⭐ |
| 练习 7 | 接口入门 | 一个类实现多个接口、接口做功能拓展 | ⭐⭐⭐ |