Răsfoiți Sursa

feat: 新增20260722继承课堂练习题(4道随堂+2道进阶),涵盖继承/多层继承/super()/方法重写/多态/对象数组/冒泡排序综合

WanJL 3 zile în urmă
părinte
comite
a36f2e4ea7
2 a modificat fișierele cu 381 adăugiri și 0 ștergeri
  1. 1 0
      README.md
  2. 380 0
      课堂练习题/20260722-继承练习.md

+ 1 - 0
README.md

@@ -55,4 +55,5 @@
 | 2026-07-17 | [Java 方法基础练习](./课堂练习题/20260717-Java方法基础练习.md) | 方法 4 种声明方式、方法调用 |
 | 2026-07-17 | [Java 方法基础练习](./课堂练习题/20260717-Java方法基础练习.md) | 方法 4 种声明方式、方法调用 |
 | 2026-07-20 | [面向对象基础练习](./课堂练习题/20260720-面向对象基础练习.md) | 类与对象、封装、包、方法、数组、Random 综合
 | 2026-07-20 | [面向对象基础练习](./课堂练习题/20260720-面向对象基础练习.md) | 类与对象、封装、包、方法、数组、Random 综合
 | 2026-07-21 | [构造方法练习](./课堂练习题/20260721-构造方法练习.md) | 构造方法概念、构造方法重载、private 构造方法、this() 构造方法间调用、构造方法+对象数组+排序+Random 综合 |
 | 2026-07-21 | [构造方法练习](./课堂练习题/20260721-构造方法练习.md) | 构造方法概念、构造方法重载、private 构造方法、this() 构造方法间调用、构造方法+对象数组+排序+Random 综合 |
+| 2026-07-22 | [继承练习](./课堂练习题/20260722-继承练习.md) | 继承概念与格式、多层继承、super() 调用父类构造方法、方法重写、is...a 关系判断、继承+构造方法+对象数组+排序+多态综合 |
 
 

+ 380 - 0
课堂练习题/20260722-继承练习.md

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