20260722-继承练习.md 16 KB

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. 思考:临时工是否适合放在这个继承体系中?为什么?

参考答案思路

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 等各种类型)

示例数据

   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);
  1. 遍历输出所有员工信息:调用每个元素的 showInfo() 方法

    思考:数组类型是 Employee[],但里面的对象实际上是 ManagerProgrammer 等,为什么调用 showInfo() 时能正确输出子类的信息?—— 这就是 多态 的体现。

  2. 按薪资从高到低排序:使用冒泡排序,按 getSalary() 的返回值降序排列

    注意:排序时比较的是 getSalary() 的返回值,不同类型的员工计算方式不同,但通过方法重写,同一个方法名在不同子类中有不同的计算逻辑

  3. 排序后再次输出所有员工信息

  4. 查找薪资最高的员工:遍历数组,找到并输出薪资最高的员工信息

涉及知识点

知识点 说明
继承 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 题:继承 + 抽象思想 + 接口雏形——图形面积计算器 ⭐⭐⭐⭐⭐

注意:本题引入了 抽象方法接口 的思想,但不需要使用 abstractinterface 关键字。通过父类定义统一方法名,子类各自实现自己的版本,体会「约定优于实现」的设计思想。

需求说明

开发一个图形面积计算器,可以计算不同形状的面积,并支持按面积排序。

类设计

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 > 0height > 0

Triangle(继承 Shape):

  • 新增私有属性base(底边,double)、height(高,double)
  • 构造方法Triangle(String color, double base, double height)
  • 重写 getArea() —— 返回 base × height / 2
  • 提示:setter 中校验 base > 0height > 0

测试类要求

ShapeCalculatorTest

  1. 创建图形数组:创建一个 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);  // 面积 = 6.0
   Shape s4 = new Circle("黄色", 3.0);          // 面积 ≈ 28.27
   Shape s5 = new Rectangle("紫色", 8.0, 2.0); // 面积 = 16.0
  1. 遍历输出所有图形信息:调用每个元素的 showInfo() 方法

  2. 按面积从大到小排序:使用冒泡排序,按 getArea() 返回值降序排列

  3. 排序后再次输出所有图形信息

  4. 查找面积最大的图形:遍历数组,输出面积最大的图形信息

思考题(不需编码,口头回答)

  1. 如果新增一个 Square(正方形)类,它应该继承哪个类?需要重写哪些方法?
  2. 父类 ShapegetArea() 返回 0.0 有什么问题?如果用 abstract 关键字强制子类重写会怎样?
  3. 为什么数组类型声明为 Shape[] 却能存储 CircleRectangle 等不同类型的对象?这体现了 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 进阶 继承 + 构造方法 + 方法重写 + 多态 + 对象数组 + 冒泡排序 + 抽象思想 ⭐⭐⭐⭐⭐