# 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 | 进阶 | 继承 + 构造方法 + 方法重写 + 多态 + 对象数组 + 冒泡排序 + 抽象思想 | ⭐⭐⭐⭐⭐ |