|
|
@@ -0,0 +1,380 @@
|
|
|
+# 2026-07-22 继承(Inheritance)基础练习(课堂练习用)
|
|
|
+
|
|
|
+> **说明**:以下习题围绕今日授课核心内容——**继承(extends)** 展开,涵盖继承的概念、格式、多层继承以及继承与构造方法/对象数组/排序的综合应用。
|
|
|
+>
|
|
|
+> 题目分为两部分:**随堂练习**(继承入门,4 题)和 **进阶练习**(综合挑战,2 题)。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第一部分:随堂练习
|
|
|
+
|
|
|
+> 以下练习围绕继承的基础概念和用法,每题独立可练。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 1:继承入门——Vehicle 车辆体系
|
|
|
+
|
|
|
+**难度**:⭐
|
|
|
+**知识点**:继承的格式、子类调用父类方法
|
|
|
+
|
|
|
+创建一个简单的车辆继承体系:
|
|
|
+
|
|
|
+**`Vehicle` 类**(父类):
|
|
|
+- **方法**:`start()` —— 输出 `"车辆启动"`
|
|
|
+- **方法**:`stop()` —— 输出 `"车辆停止"`
|
|
|
+
|
|
|
+**`Car` 类**(子类,继承 Vehicle):
|
|
|
+- **方法**:`honk()` —— 输出 `"汽车鸣笛:滴滴!"`
|
|
|
+
|
|
|
+**`Bike` 类**(子类,继承 Vehicle):
|
|
|
+- **方法**:`ringBell()` —— 输出 `"自行车铃铛:叮铃铃!"`
|
|
|
+
|
|
|
+**测试类要求**:
|
|
|
+1. 创建 `Car` 对象,调用 `start()`、`honk()`、`stop()` 方法
|
|
|
+2. 创建 `Bike` 对象,调用 `start()`、`ringBell()`、`stop()` 方法
|
|
|
+3. 观察子类是否可以直接使用父类的方法
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+车辆启动
|
|
|
+汽车鸣笛:滴滴!
|
|
|
+车辆停止
|
|
|
+车辆启动
|
|
|
+自行车铃铛:叮铃铃!
|
|
|
+车辆停止
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 2:多层继承——Person → Student → Postgraduate
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:多层继承、方法追加
|
|
|
+
|
|
|
+创建一个三层继承体系:
|
|
|
+
|
|
|
+**`Person` 类**(父类):
|
|
|
+- **私有属性**:`name`(姓名)、`age`(年龄)
|
|
|
+- **全参构造方法**:`Person(String name, int age)`
|
|
|
+- **getter/setter**
|
|
|
+- **方法**:`showInfo()` —— 输出 `"姓名:xxx,年龄:xxx"`
|
|
|
+
|
|
|
+**`Student` 类**(继承 Person):
|
|
|
+- **新增私有属性**:`school`(学校,String)
|
|
|
+- **全参构造方法**:`Student(String name, int age, String school)`
|
|
|
+ - 使用 `super(name, age)` 调用父类构造方法
|
|
|
+- **getter/setter**
|
|
|
+- **重写** `showInfo()` —— 输出 `"姓名:xxx,年龄:xxx,学校:xxx"`
|
|
|
+
|
|
|
+**`Postgraduate` 类**(继承 Student):
|
|
|
+- **新增私有属性**:`mentor`(导师,String)
|
|
|
+- **全参构造方法**:`Postgraduate(String name, int age, String school, String mentor)`
|
|
|
+ - 使用 `super(name, age, school)` 调用父类构造方法
|
|
|
+- **getter/setter**
|
|
|
+- **重写** `showInfo()` —— 输出 `"姓名:xxx,年龄:xxx,学校:xxx,导师:xxx"`
|
|
|
+
|
|
|
+**测试类要求**:
|
|
|
+1. 创建 `Person` 对象,调用 `showInfo()`
|
|
|
+2. 创建 `Student` 对象,调用 `showInfo()`
|
|
|
+3. 创建 `Postgraduate` 对象,调用 `showInfo()`
|
|
|
+
|
|
|
+**示例输出**:
|
|
|
+```
|
|
|
+姓名:张三,年龄:30
|
|
|
+姓名:李四,年龄:22,学校:清华大学
|
|
|
+姓名:王五,年龄:25,学校:北京大学,导师:陈教授
|
|
|
+```
|
|
|
+
|
|
|
+> **提示**:子类构造方法中使用 `super(参数)` 调用父类构造方法,必须写在构造方法的第一行。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 3:继承 + 构造方法 + 方法重写——Animal 体系
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:继承 + 构造方法 + 方法重写(Override)
|
|
|
+
|
|
|
+创建动物继承体系:
|
|
|
+
|
|
|
+**`Animal` 类**(父类):
|
|
|
+- **私有属性**:`name`(名字)、`age`(年龄)
|
|
|
+- **全参构造方法**:`Animal(String name, int age)`
|
|
|
+- **getter/setter**
|
|
|
+- **方法**:`makeSound()` —— 输出 `"动物发出声音"`
|
|
|
+
|
|
|
+**`Dog` 类**(继承 Animal):
|
|
|
+- **新增私有属性**:`breed`(品种,String)
|
|
|
+- **全参构造方法**:`Dog(String name, int age, String breed)`
|
|
|
+ - 使用 `super(name, age)` 调用父类构造方法
|
|
|
+- **getter/setter**
|
|
|
+- **重写** `makeSound()` —— 输出 `"狗叫:汪汪汪!"`
|
|
|
+- **方法**:`guard()` —— 输出 `"狗狗在看家护院"`
|
|
|
+
|
|
|
+**`Cat` 类**(继承 Animal):
|
|
|
+- **新增私有属性**:`color`(颜色,String)
|
|
|
+- **全参构造方法**:`Cat(String name, int age, String color)`
|
|
|
+ - 使用 `super(name, age)` 调用父类构造方法
|
|
|
+- **getter/setter**
|
|
|
+- **重写** `makeSound()` —— 输出 `"猫叫:喵喵喵!"`
|
|
|
+- **方法**:`scratch()` —— 输出 `"猫咪在抓沙发"`
|
|
|
+
|
|
|
+**测试类要求**:
|
|
|
+1. 创建 `Dog` 对象(如:"旺财", 3, "金毛"),调用 `makeSound()` 和 `guard()`
|
|
|
+2. 创建 `Cat` 对象(如:"咪咪", 2, "橘色"),调用 `makeSound()` 和 `scratch()`
|
|
|
+3. 观察:相同的方法名 `makeSound()`,在不同的子类中有不同的行为——这就是 **方法重写(Override)**
|
|
|
+
|
|
|
+**示例输出**:
|
|
|
+```
|
|
|
+狗叫:汪汪汪!
|
|
|
+狗狗在看家护院
|
|
|
+猫叫:喵喵喵!
|
|
|
+猫咪在抓沙发
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 4:判断 is...a 关系——员工体系设计
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:is...a 关系判断、继承设计合理性
|
|
|
+
|
|
|
+根据以下描述,设计合理的类继承体系:
|
|
|
+
|
|
|
+**场景**:某公司有以下几类人员:
|
|
|
+- **员工**(Employee):所有人员都具有工号(id)、姓名(name)、基本工资(baseSalary)
|
|
|
+- **经理**(Manager):在员工基础上,有岗位津贴(bonus)
|
|
|
+- **程序员**(Programmer):在员工基础上,有项目奖金(projectBonus)
|
|
|
+- **设计师**(Designer):在员工基础上,有设计提成(commission)
|
|
|
+- **临时工**(TempWorker):只有工号、姓名、时薪(hourlyWage)和工时(hours),**没有**基本工资
|
|
|
+
|
|
|
+**要求**:
|
|
|
+1. 用 **继承** 设计以上类的层次结构
|
|
|
+2. 画出继承关系图(文字描述即可)
|
|
|
+3. 写出每个类的属性列表(哪些属性定义在父类,哪些定义在子类)
|
|
|
+4. 思考:临时工是否适合放在这个继承体系中?为什么?
|
|
|
+
|
|
|
+**参考答案思路**:
|
|
|
+```text
|
|
|
+Employee(工号、姓名、基本工资)
|
|
|
+ ├── Manager(岗位津贴)
|
|
|
+ ├── Programmer(项目奖金)
|
|
|
+ └── Designer(设计提成)
|
|
|
+
|
|
|
+TempWorker(工号、姓名、时薪、工时)—— 不适合继承 Employee,因为没有基本工资
|
|
|
+```
|
|
|
+
|
|
|
+> **思考**:如果让 TempWorker 继承 Employee,会出现什么问题?
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第二部分:进阶练习
|
|
|
+
|
|
|
+> 以下为综合挑战题,融合继承与构造方法、对象数组、排序、方法重写等知识点。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 第 1 题:继承 + 对象数组 + 多态——Employee 薪资管理系统 ⭐⭐⭐⭐
|
|
|
+
|
|
|
+综合运用继承、构造方法、对象数组、冒泡排序、方法重写、**多态**等知识点。
|
|
|
+
|
|
|
+### 需求说明
|
|
|
+
|
|
|
+某公司需要对员工进行统一的薪资管理。不同类型的员工计算薪资的方式不同:
|
|
|
+
|
|
|
+| 员工类型 | 薪资计算方式 |
|
|
|
+|---------|------------|
|
|
|
+| **普通员工** (Employee) | `baseSalary`(基本工资) |
|
|
|
+| **经理** (Manager) | `baseSalary + bonus`(基本工资 + 岗位津贴) |
|
|
|
+| **程序员** (Programmer) | `baseSalary + projectBonus`(基本工资 + 项目奖金) |
|
|
|
+| **设计师** (Designer) | `baseSalary + commission`(基本工资 + 设计提成) |
|
|
|
+
|
|
|
+### 类设计
|
|
|
+
|
|
|
+**`Employee` 类**(父类):
|
|
|
+- **私有属性**:`id`(工号,int)、`name`(姓名,String)、`baseSalary`(基本工资,double)
|
|
|
+- **全参构造方法**:`Employee(int id, String name, double baseSalary)`
|
|
|
+- **getter/setter**
|
|
|
+- **方法**:`getSalary()` —— 返回 `baseSalary`(普通员工就是基本工资)
|
|
|
+- **方法**:`showInfo()` —— 输出 `"工号:xxx,姓名:xxx,薪资:xxx"`
|
|
|
+
|
|
|
+**`Manager` 类**(继承 Employee):
|
|
|
+- **新增私有属性**:`bonus`(岗位津贴,double)
|
|
|
+- **全参构造方法**:`Manager(int id, String name, double baseSalary, double bonus)`
|
|
|
+- **重写** `getSalary()` —— 返回 `baseSalary + bonus`
|
|
|
+- **重写** `showInfo()` —— 输出 `"工号:xxx,姓名:xxx,职位:经理,薪资:xxx"`
|
|
|
+
|
|
|
+**`Programmer` 类**(继承 Employee):
|
|
|
+- **新增私有属性**:`projectBonus`(项目奖金,double)
|
|
|
+- **全参构造方法**:`Programmer(int id, String name, double baseSalary, double projectBonus)`
|
|
|
+- **重写** `getSalary()` —— 返回 `baseSalary + projectBonus`
|
|
|
+- **重写** `showInfo()` —— 输出 `"工号:xxx,姓名:xxx,职位:程序员,薪资:xxx"`
|
|
|
+
|
|
|
+**`Designer` 类**(继承 Employee):
|
|
|
+- **新增私有属性**:`commission`(设计提成,double)
|
|
|
+- **全参构造方法**:`Designer(int id, String name, double baseSalary, double commission)`
|
|
|
+- **重写** `getSalary()` —— 返回 `baseSalary + commission`
|
|
|
+- **重写** `showInfo()` —— 输出 `"工号:xxx,姓名:xxx,职位:设计师,薪资:xxx"`
|
|
|
+
|
|
|
+### 测试类要求
|
|
|
+
|
|
|
+**`SalarySystemTest` 类**:
|
|
|
+
|
|
|
+1. **创建员工数组**:创建一个 `Employee[]` 数组,包含至少 5 个员工(包含 Manager、Programmer、Designer 等各种类型)
|
|
|
+
|
|
|
+ **示例数据**:
|
|
|
+ ```java
|
|
|
+ Employee e1 = new Manager(1001, "张经理", 15000, 5000);
|
|
|
+ Employee e2 = new Programmer(2001, "李编程", 12000, 3000);
|
|
|
+ Employee e3 = new Designer(3001, "王设计", 10000, 4000);
|
|
|
+ Employee e4 = new Employee(4001, "赵普通", 8000);
|
|
|
+ Employee e5 = new Programmer(2002, "刘代码", 11000, 2500);
|
|
|
+ ```
|
|
|
+
|
|
|
+2. **遍历输出所有员工信息**:调用每个元素的 `showInfo()` 方法
|
|
|
+ > **思考**:数组类型是 `Employee[]`,但里面的对象实际上是 `Manager`、`Programmer` 等,为什么调用 `showInfo()` 时能正确输出子类的信息?—— 这就是 **多态** 的体现。
|
|
|
+
|
|
|
+3. **按薪资从高到低排序**:使用冒泡排序,按 `getSalary()` 的返回值降序排列
|
|
|
+ > **注意**:排序时比较的是 `getSalary()` 的返回值,不同类型的员工计算方式不同,但通过方法重写,**同一个方法名在不同子类中有不同的计算逻辑**。
|
|
|
+
|
|
|
+4. **排序后再次输出所有员工信息**
|
|
|
+
|
|
|
+5. **查找薪资最高的员工**:遍历数组,找到并输出薪资最高的员工信息
|
|
|
+
|
|
|
+### 涉及知识点
|
|
|
+
|
|
|
+| 知识点 | 说明 |
|
|
|
+|--------|------|
|
|
|
+| **继承** | Employee 作为父类,Manager/Programmer/Designer 作为子类 |
|
|
|
+| **构造方法 + super()** | 子类构造方法中调用 `super(id, name, baseSalary)` |
|
|
|
+| **方法重写(Override)** | `getSalary()` 和 `showInfo()` 在子类中重写 |
|
|
|
+| **多态** | 父类引用指向子类对象,调用重写方法时执行子类版本 |
|
|
|
+| **对象数组** | `Employee[]` 存储不同类型的员工对象 |
|
|
|
+| **冒泡排序** | 按 `getSalary()` 返回值降序排序 |
|
|
|
+| **遍历查找** | 查找薪资最高的员工 |
|
|
|
+
|
|
|
+### 预期输出(参考)
|
|
|
+
|
|
|
+```
|
|
|
+========== 排序前 ==========
|
|
|
+工号:1001,姓名:张经理,职位:经理,薪资:20000.0
|
|
|
+工号:2001,姓名:李编程,职位:程序员,薪资:15000.0
|
|
|
+工号:3001,姓名:王设计,职位:设计师,薪资:14000.0
|
|
|
+工号:4001,姓名:赵普通,职位:员工,薪资:8000.0
|
|
|
+工号:2002,姓名:刘代码,职位:程序员,薪资:13500.0
|
|
|
+
|
|
|
+========== 排序后(按薪资降序) ==========
|
|
|
+工号:1001,姓名:张经理,职位:经理,薪资:20000.0
|
|
|
+工号:2001,姓名:李编程,职位:程序员,薪资:15000.0
|
|
|
+工号:3001,姓名:王设计,职位:设计师,薪资:14000.0
|
|
|
+工号:2002,姓名:刘代码,职位:程序员,薪资:13500.0
|
|
|
+工号:4001,姓名:赵普通,职位:员工,薪资:8000.0
|
|
|
+
|
|
|
+========== 最高薪资员工 ==========
|
|
|
+工号:1001,姓名:张经理,职位:经理,薪资:20000.0
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 第 2 题:继承 + 抽象思想 + 接口雏形——图形面积计算器 ⭐⭐⭐⭐⭐
|
|
|
+
|
|
|
+> **注意**:本题引入了 **抽象方法** 和 **接口** 的思想,但不需要使用 `abstract` 或 `interface` 关键字。通过父类定义统一方法名,子类各自实现自己的版本,体会「约定优于实现」的设计思想。
|
|
|
+
|
|
|
+### 需求说明
|
|
|
+
|
|
|
+开发一个图形面积计算器,可以计算不同形状的面积,并支持按面积排序。
|
|
|
+
|
|
|
+### 类设计
|
|
|
+
|
|
|
+**`Shape` 类**(父类):
|
|
|
+- **私有属性**:`color`(颜色,String)
|
|
|
+- **有参构造方法**:`Shape(String color)`
|
|
|
+- **getter/setter**
|
|
|
+- **方法**:`getArea()` —— 暂时返回 `0.0`(作为默认实现,子类会重写)
|
|
|
+- **方法**:`showInfo()` —— 输出 `"颜色:xxx,面积:xxx"`,其中面积通过调用 `getArea()` 获得
|
|
|
+
|
|
|
+**`Circle` 类**(继承 Shape):
|
|
|
+- **新增私有属性**:`radius`(半径,double)
|
|
|
+- **构造方法**:`Circle(String color, double radius)`
|
|
|
+- **重写** `getArea()` —— 返回 `π × r²`(π 取 `3.1415926`)
|
|
|
+- **提示**:setter 中校验 `radius > 0`,否则提示 `"半径必须大于0"`
|
|
|
+
|
|
|
+**`Rectangle` 类**(继承 Shape):
|
|
|
+- **新增私有属性**:`width`(宽度,double)、`height`(高度,double)
|
|
|
+- **构造方法**:`Rectangle(String color, double width, double height)`
|
|
|
+- **重写** `getArea()` —— 返回 `width × height`
|
|
|
+- **提示**:setter 中校验 `width > 0` 且 `height > 0`
|
|
|
+
|
|
|
+**`Triangle` 类**(继承 Shape):
|
|
|
+- **新增私有属性**:`base`(底边,double)、`height`(高,double)
|
|
|
+- **构造方法**:`Triangle(String color, double base, double height)`
|
|
|
+- **重写** `getArea()` —— 返回 `base × height / 2`
|
|
|
+- **提示**:setter 中校验 `base > 0` 且 `height > 0`
|
|
|
+
|
|
|
+### 测试类要求
|
|
|
+
|
|
|
+**`ShapeCalculatorTest` 类**:
|
|
|
+
|
|
|
+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); // 面积 = 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. 如果新增一个 `Square`(正方形)类,它应该继承哪个类?需要重写哪些方法?
|
|
|
+2. 父类 `Shape` 的 `getArea()` 返回 `0.0` 有什么问题?如果用 `abstract` 关键字强制子类重写会怎样?
|
|
|
+3. 为什么数组类型声明为 `Shape[]` 却能存储 `Circle`、`Rectangle` 等不同类型的对象?这体现了 OOP 的哪个特征?
|
|
|
+
|
|
|
+### 预期输出(参考)
|
|
|
+
|
|
|
+```
|
|
|
+========== 排序前 ==========
|
|
|
+颜色:红色,面积:78.54
|
|
|
+颜色:蓝色,面积:24.0
|
|
|
+颜色:绿色,面积:6.0
|
|
|
+颜色:黄色,面积:28.27
|
|
|
+颜色:紫色,面积:16.0
|
|
|
+
|
|
|
+========== 排序后(按面积降序) ==========
|
|
|
+颜色:红色,面积:78.54
|
|
|
+颜色:黄色,面积:28.27
|
|
|
+颜色:蓝色,面积:24.0
|
|
|
+颜色:紫色,面积:16.0
|
|
|
+颜色:绿色,面积:6.0
|
|
|
+
|
|
|
+========== 面积最大的图形 ==========
|
|
|
+颜色:红色,面积:78.54
|
|
|
+```
|
|
|
+
|
|
|
+> **提示**:
|
|
|
+> - 本题所有类的构造方法中都需要调用 `super(color)` 来初始化父类的属性
|
|
|
+> - 排序时使用冒泡排序,比较的是 `getArea()` 的返回值
|
|
|
+> - 面积计算使用 `double` 类型,输出时保留两位小数即可(`System.out.printf("%.2f", area)`)
|
|
|
+> - 本题引入了「多态」的思想——相同的 `Shape[]` 数组,存储不同的子类对象,调用相同的方法名 `getArea()`,但执行不同的计算逻辑。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 📋 题目总览
|
|
|
+
|
|
|
+| 序号 | 所属部分 | 核心知识点 | 难度 |
|
|
|
+|------|---------|-----------|------|
|
|
|
+| 练习 1 | 随堂 | 继承格式、子类调用父类方法 | ⭐ |
|
|
|
+| 练习 2 | 随堂 | 多层继承、super() 调用父类构造方法、方法重写 | ⭐⭐ |
|
|
|
+| 练习 3 | 随堂 | 继承 + 构造方法 + 方法重写 | ⭐⭐ |
|
|
|
+| 练习 4 | 随堂 | is...a 关系判断、继承设计合理性 | ⭐⭐ |
|
|
|
+| 进阶 1 | 进阶 | 继承 + 构造方法 + super() + 方法重写 + 多态 + 对象数组 + 冒泡排序 | ⭐⭐⭐⭐ |
|
|
|
+| 进阶 2 | 进阶 | 继承 + 构造方法 + 方法重写 + 多态 + 对象数组 + 冒泡排序 + 抽象思想 | ⭐⭐⭐⭐⭐ |
|