20260721-构造方法练习.md 24 KB

2026-07-21 构造方法(构造器)基础练习(课堂练习用)

说明:以下习题围绕今日授课核心内容——构造方法(构造器) 展开,涵盖构造方法的概念、格式、重载、private 构造方法以及构造方法与封装的综合应用。

题目分为两部分:随堂练习(构造方法入门,6 题)和 进阶练习(综合挑战,10 题)。


第一部分:随堂练习

以下练习围绕构造方法的基础概念和用法,每题独立可练。


练习 1:无参构造方法——Cat 类

难度:⭐
知识点:类定义、无参构造方法

创建一个 Cat 类,包含:

  • 私有属性name(String)、age(int)
  • 无参构造方法:输出一条消息 "一只小猫出生了!",并将 name 赋值为 "小咪"age 赋值为 1
  • getter/setter 方法

测试类要求:创建 Cat 对象,通过 getter 输出 name 和 age 的值。

预期输出

一只小猫出生了!
name = 小咪, age = 1

练习 2:有参构造方法——Dog 类

难度:⭐
知识点:有参构造方法

创建一个 Dog 类,包含:

  • 私有属性name(String)、breed(品种,String)
  • 有参构造方法Dog(String name, String breed) —— 通过参数为属性赋值
  • 方法showInfo() —— 输出 "品种:xxx,名字:xxx"

测试类要求:使用有参构造方法创建 2 个 Dog 对象,分别调用 showInfo() 输出信息。

示例输出

品种:金毛,名字:旺财
品种:哈士奇,名字:二哈

练习 3:构造方法重载——Book 类

难度:⭐⭐
知识点:构造方法重载

创建一个 Book 类,包含:

  • 私有属性title(书名,String)、author(作者,String)、price(价格,double)
  • 3 个重载的构造方法
    • 无参构造:书名 = "未知",作者 = "未知",价格 = 0.0
    • 双参构造:Book(String title, String author) —— 书名和作者由参数传入,价格默认为 0.0
    • 全参构造:Book(String title, String author, double price) —— 所有属性由参数传入
  • 方法showInfo() 输出所有属性信息

测试类要求:分别使用 3 种构造方法创建 3 个 Book 对象,调用 showInfo() 输出。

示例输出

书名:未知,作者:未知,价格:0.0
书名:Java从入门到精通,作者:张三,价格:0.0
书名:设计模式,作者:李四,价格:59.0

练习 4:this 关键字在构造方法中应用——Phone 类

难度:⭐⭐
知识点:构造方法 + this 关键字

创建一个 Phone 类,包含:

  • 私有属性brand(品牌,String)、price(价格,double)、color(颜色,String)
  • 构造方法:全参构造 Phone(String brand, double price, String color) —— 使用 this 关键字区分同名参数
  • 方法call(String name) —— 输出 "品牌xxx的手机正在给yyy打电话"

测试类要求:使用全参构造创建对象,调用 call() 方法。

示例输出

品牌华为的手机正在给张三打电话

练习 5:private 构造方法——Singleton 体验

难度:⭐⭐⭐
知识点:private 构造方法

创建一个 Manager 类,包含:

  • 私有属性name(String)
  • 私有构造方法private Manager() —— 构造方法内部输出 "Manager 对象已创建"
  • 公有 static 方法:public static Manager getInstance() —— 内部 new Manager() 并返回该对象
  • getter/setter

测试类要求

  1. 尝试直接 new Manager(),观察编译错误
  2. 通过 Manager.getInstance() 方法获取 Manager 对象,设置 name 并输出

提示:这是单例设计模式的雏形——通过私有构造方法 + 静态方法控制对象的创建。


练习 6:默认构造方法 vs 显式定义

难度:⭐⭐
知识点:默认构造方法被覆盖

创建一个 StudentV3 类,包含:

  • 私有属性name(String)、score(int)
  • 定义一个 2 参构造方法StudentV3(String name, int score)
  • 注意:不要定义无参构造方法

测试类要求

  1. 尝试 new StudentV3() —— 观察编译是否报错(因为显式定义了构造方法,默认无参构造被覆盖)
  2. 使用 new StudentV3("小明", 90) 创建对象,通过 getter 输出信息

思考:为什么 new StudentV3() 会报错?


第二部分:进阶练习

以下为综合挑战题,融合构造方法与封装、对象数组、Random、方法等知识点,难度逐步递增。


第 1 题:构造方法 + 封装——Computer 类 ⭐

创建一个 Computer 类,包含:

  • 私有属性brand(品牌)、cpu(CPU 型号)、memory(内存大小,int,单位 GB)、price(价格)
  • 无参构造方法:品牌 = "未知品牌",CPU = "未知CPU",内存 = 8,价格 = 3999.0
  • 全参构造方法:所有属性通过参数传入
  • getter/setter
  • 方法showConfig() 输出电脑配置信息

测试类要求:分别使用无参构造和全参构造创建两个 Computer 对象,调用 showConfig() 输出。


第 2 题:构造方法 + 对象数组——创建多个对象 ⭐⭐

定义一个 Hero(英雄)类,包含:

  • 私有属性name(英雄名)、hp(血量,int)、attack(攻击力,int)
  • 全参构造方法Hero(String name, int hp, int attack)
  • getter/setter
  • 方法showInfo() 输出英雄信息

测试类要求

  1. 创建 5 个 Hero 对象(数据自拟:如"盖伦" 580 100、"剑圣" 450 130 等)
  2. 存入 Hero[] 数组
  3. 遍历数组,调用每个英雄的 showInfo() 方法

第 3 题:构造方法 + setter 校验——BankAccount 类 ⭐⭐

创建一个 BankAccount 类,包含:

  • 私有属性accountNo(账号,String)、owner(户主,String)、balance(余额,double)
  • 全参构造方法BankAccount(String accountNo, String owner, double balance)
  • 在构造方法中加入校验:如果 balance 小于 0,则输出 "初始余额不能为负数,已设置为0.0",并将余额强制设为 0.0
  • getter/setter(setBalance 也要有同样的校验)
  • 方法deposit(double amount) 存款(金额必须大于0),withdraw(double amount) 取款(不能透支)

测试类要求

  1. 使用全参构造创建一个余额为负数的账户,观察构造方法中的校验输出
  2. 测试存款、取款功能

第 4 题:构造方法重载 + 默认值——Employee 类 ⭐⭐

创建一个 Employee 类,包含:

  • 私有属性id(工号)、name(姓名)、position(职位)、salary(薪资)
  • 3 个重载的构造方法
    • 全参构造
    • Employee(String id, String name, String position) —— 薪资默认为 5000.0
    • Employee(String id, String name) —— 职位默认 "普通员工",薪资默认 3500.0
  • 方法work() 输出 "工号xxx的员工xxx正在工作,职位:xxx,薪资:xxx"

测试类要求:使用 3 种构造方法分别创建对象,调用 work() 方法。


第 5 题:构造方法 + 方法调用——Circle ⭐⭐

创建一个 Circle 类,包含:

  • 私有属性radius(半径,double)
  • 构造方法
    • 无参构造:半径 = 1.0
    • 有参构造:Circle(double radius)
  • setter 中校验半径必须大于 0,否则提示 "半径必须大于0"
  • 方法
    • getArea() 返回面积(π × r²)
    • getPerimeter() 返回周长(2 × π × r)

测试类要求:创建两个 Circle 对象(一个无参,一个有参),分别输出面积和周长。

提示:π 取 3.1415926,面积和周长的返回值为 double 类型。


第 6 题:构造方法 + this() 调用——Student 类 ⭐⭐⭐

注意this(参数) 在一个构造方法中调用另一个构造方法,必须写在构造方法的第一行。

创建一个 StudentV4 类,包含:

  • 私有属性id(学号,int)、name(姓名,String)、className(班级,String)
  • 构造方法(使用 this() 互相调用):
    • 无参构造:调用全参构造,传入 0, "未知", "未分配"
    • StudentV4(int id, String name):调用全参构造,传入 id, name, "默认班"
    • StudentV4(int id, String name, String className):直接为属性赋值
  • 方法showInfo() 输出所有信息

测试类要求:分别使用 3 种构造方法创建对象,输出验证。

示例输出

学号:0,姓名:未知,班级:未分配
学号:1001,姓名:张三,班级:默认班
学号:1002,姓名:李四,班级:J260713

第 7 题:构造方法 + 对象数组 + 遍历打印——Movie 类 ⭐⭐⭐

创建一个 Movie(电影)类,包含:

  • 私有属性title(片名)、director(导演)、year(上映年份,int)、rating(评分,double)
  • 全参构造方法
  • getter/setter
  • 方法showInfo() 输出电影信息

测试类要求

  1. 创建一个 Movie[] 数组,存储至少 4 部电影
  2. 使用 全参构造方法一次性初始化 所有电影对象(不要用无参构造 + setter)
  3. 遍历数组,输出评分最高的电影信息

示例数据

"肖申克的救赎", "弗兰克·德拉邦特", 1994, 9.7
"霸王别姬", "陈凯歌", 1993, 9.6
"阿甘正传", "罗伯特·泽米吉斯", 1994, 9.5
"泰坦尼克号", "詹姆斯·卡梅隆", 1997, 9.4

第 8 题:构造方法 + Random + 方法——猜数游戏(面向对象版)⭐⭐⭐⭐

将猜数字游戏改造为面向对象版本,使用构造方法初始化游戏:

GuessGame

  • 私有属性target(目标数字,int)、maxRange(最大范围,int)、attempts(已猜次数,int)
  • 构造方法GuessGame(int maxRange) —— 在 1 ~ maxRange 之间用 Random 生成目标数字
  • 方法
    • guess(int num) —— 判断猜测结果,返回 String"猜对了!" / "大了,再小点" / "小了,再大点"
    • getAttempts() —— 返回已猜次数

GuessGameTest 测试类

  1. 创建 GuessGame 对象,范围 1~100
  2. 使用 Scanner 让用户循环输入猜测数字
  3. 调用 guess() 方法判断,直到猜对为止
  4. 猜对后输出总共猜了多少次

第 9 题:构造方法 + 封装 + 对象数组 + 排序——BookManager ⭐⭐⭐⭐

创建一个 BookV2 类:

  • 私有属性id(编号,int)、title(书名)、price(价格,double)
  • 全参构造方法
  • getter/setter

创建一个 BookManager 类,包含以下方法:

  • initBooks() —— 创建并返回一个包含 5 本图书的 BookV2[] 数组(使用全参构造初始化)
  • printBooks(BookV2[] books) —— 遍历输出所有图书信息
  • sortByPrice(BookV2[] books) —— 按价格从高到低排序(冒泡排序)
  • searchByTitle(BookV2[] books, String title) —— 根据书名查找图书,返回找到的图书对象(找不到返回 null

BookManagerTest 测试类

  1. 调用 initBooks() 初始化图书数组
  2. 调用 printBooks() 输出原始顺序
  3. 调用 sortByPrice() 排序后再次输出
  4. 调用 searchByTitle() 查找某本书,输出查找结果

第 10 题:综合大挑战——学生成绩管理系统(构造方法版)⭐⭐⭐⭐⭐

使用构造方法完成完整的学生成绩管理系统,在包 system 下创建以下类:

StudentV5(实体类):

  • 私有属性id(学号,int)、name(姓名,String)、javaScore(Java 成绩,double)、mysqlScore(MySQL 成绩,double)
  • 全参构造方法
  • getter/setter
  • 方法getAverage() 返回两门课的平均分

StudentSystem(业务类):

  • 私有属性StudentV5[] students(学生数组)、count(当前学生数量)
  • 构造方法StudentSystem(int capacity) —— 初始化指定容量的学生数组
  • 方法
    • addStudent(StudentV5 s) —— 将学生添加到数组中(如果数组已满,提示"数组已满,添加失败")
    • printAll() —— 输出所有学生信息(含平均分)
    • sortByAverage() —— 按平均分降序排序(冒泡排序)
    • getTopStudent() —— 返回平均分最高的学生对象

SystemTest(测试类):

  1. 创建 StudentSystem 对象,容量为 5
  2. 使用全参构造创建 5 个学生对象
  3. 调用 addStudent() 逐个添加学生
  4. 调用 printAll() 输出所有学生信息
  5. 调用 sortByAverage() 排序后再次输出
  6. 调用 getTopStudent() 输出最高分学生信息

第 11 题:综合大挑战——MyArray 可变数组(动态数组底层实现)⭐⭐⭐⭐⭐

模仿 ArrayList 的底层原理,自己动手实现一个可变数组(动态数组)。本题综合运用 构造方法、数组扩容、方法封装、排序算法 等核心知识点。

需求背景

Java 中普通数组 int[] 一旦创建,长度就固定了,无法动态增减元素。请自行实现一个 可变整型数组 MyArray 类,内部使用 int[] 存储数据,对外提供便捷的增删改查方法。


实体类说明:MyArray

私有属性

属性 类型 说明
data int[] 内部真正存储数据的数组
size int 当前已存储的元素个数(不是数组长度)

构造方法

构造方法 说明
MyArray() 无参构造,默认初始化容量为 10 的数组
MyArray(int capacity) 指定初始容量,如果传入的容量 ≤ 0,则使用默认容量 10

提示:构造方法中只需 data = new int[capacity]; size = 0;,不要预填数据。


核心方法列表

要求实现以下 11 个方法

1. 添加元素 — add(int element)

将元素添加到数组的末尾。

  • 添加前先检查内部数组是否已满(size == data.length
  • 如果已满,自动扩容为当前容量的 1.5 倍(即 data.length * 3 / 2
  • 扩容步骤:创建新数组 → 将旧数组元素复制到新数组 → 将新数组赋值给 data
  • 最后将新元素存入 data[size]size 自增 1

2. 获取元素 — get(int index)

返回指定下标处的元素值。

  • 如果下标越界(index < 0 || index >= size),输出 "下标越界,无法获取元素",返回 -1
  • 否则返回 data[index]

3. 修改元素 — set(int index, int element)

将指定下标处的元素修改为新值。

  • 如果下标越界,输出 "下标越界,无法修改元素",不执行修改
  • 否则将 data[index] 赋值为 element

4. 获取长度 — size()

返回当前已存储的元素个数(size 属性值)。

5. 获取容量 — capacity()

返回内部数组的总长度(data.length)。

6. 判断是否为空 — isEmpty()

如果 size == 0 返回 true,否则返回 false

7. 查找元素下标 — indexOf(int element)

从前向后遍历数组,查找第一个值与 element 相等的元素。

  • 如果找到,返回该元素的下标
  • 如果没找到,返回 -1

8. 判断是否包含 — contains(int element)

调用 indexOf() 方法,根据返回值是否为 -1 来判断。

9. 删除指定位置的元素 — remove(int index)

删除指定下标处的元素。

  • 如果下标越界,输出 "下标越界,无法删除元素",返回 -1
  • 否则:
    • 用临时变量保存被删除的元素值
    • index 之后的所有元素向前移动一位(data[j] = data[j+1]
    • size 自减 1
    • 返回被删除的元素值

示意图(删除下标 2 的元素):

> 删除前:[10, 20, 30, 40, 50]    size=5
> 删除后:[10, 20, 40, 50, 0]     size=4
> ```

#### 10. 删除指定值的元素 — `removeByValue(int element)`

删除数组中第一个值与 `element` 相等的元素。

- 调用 `indexOf(element)` 获取下标
- 如果下标为 `-1`,输出 `"未找到该元素,删除失败"`,返回 `false`
- 否则调用 `remove(index)` 方法,返回 `true`

#### 11. 数组排序 — `sort(boolean ascending)`

对数组中的元素进行排序。

- 参数 `ascending` 为 `true` 时**升序排列**(小→大),为 `false` 时**降序排列**(大→小)
- 使用**冒泡排序**或**选择排序**实现
- 注意:只对已存储的元素(`data[0] ~ data[size-1]`)排序,不要动后面的空闲位置

---

### 辅助方法

#### `print()`

遍历输出数组中的所有元素,格式为 `[元素1, 元素2, 元素3, ...]`。

- 如果数组为空,输出 `[]`
- 最后一个元素后面不要有逗号

#### `clear()`

清空数组中的所有元素,将 `size` 置为 `0`(内部数组对象保留,不清除)。

---

### 参考代码骨架

java public class MyArray {

private int[] data;  // 内部存储数组
private int size;    // 当前元素个数
// 无参构造:默认容量 10
public MyArray() {
    // TODO
}

// 有参构造:指定初始容量
public MyArray(int capacity) {
    // TODO(capacity <= 0 时使用默认容量 10)
}

// 添加元素(自动扩容)
public void add(int element) {
    // TODO
}

// 获取指定位置的元素
public int get(int index) {
    // TODO(下标越界检查)
    return 0;  // placeholder
}

// 修改指定位置的元素
public void set(int index, int element) {
    // TODO
}

// 获取元素个数
public int size() {
    return size;
}

// 获取数组容量
public int capacity() {
    return data.length;
}

// 判断是否为空
public boolean isEmpty() {
    // TODO
    return false;
}

// 查找元素下标
public int indexOf(int element) {
    // TODO
    return -1;
}

// 判断是否包含某元素
public boolean contains(int element) {
    // TODO
    return false;
}

// 删除指定位置的元素,返回被删除的值
public int remove(int index) {
    // TODO(下标越界检查)
    return 0;  // placeholder
}

// 删除指定值的元素(第一个匹配项)
public boolean removeByValue(int element) {
    // TODO
    return false;
}

// 排序(true 升序,false 降序)
public void sort(boolean ascending) {
    // TODO(冒泡排序)
}

// 遍历输出
public void print() {
    // TODO
}

// 清空数组
public void clear() {
    // TODO
}

}


---

### 测试类要求:MyArrayTest

在 `main` 方法中按顺序完成以下操作,并观察输出是否符合预期:

java public class MyArrayTest {

public static void main(String[] args) {
    // 1. 创建一个初始容量为 3 的 MyArray 对象
    MyArray arr = new MyArray(3);

    // 2. 输出初始状态
    System.out.println("初始容量:" + arr.capacity());   // 3
    System.out.println("是否为空:" + arr.isEmpty());     // true

    // 3. 添加 5 个元素(触发自动扩容)
    for (int i = 1; i <= 5; i++) {
        arr.add(i * 10);  // 添加 10, 20, 30, 40, 50
    }
    arr.print();           // [10, 20, 30, 40, 50]

    // 4. 测试各个方法
    System.out.println("元素个数:" + arr.size());        // 5
    System.out.println("当前容量:" + arr.capacity());     // 4(3*1.5=4,再扩容 4*1.5=6)
    System.out.println("下标2的元素:" + arr.get(2));     // 30
    System.out.println("是否包含40:" + arr.contains(40)); // true
    System.out.println("50的下标:" + arr.indexOf(50));   // 4

    // 5. 测试修改
    arr.set(1, 99);
    arr.print();           // [10, 99, 30, 40, 50]

    // 6. 测试删除
    int deleted = arr.remove(2);
    System.out.println("被删除的元素:" + deleted);       // 30
    arr.print();           // [10, 99, 40, 50]

    // 7. 测试按值删除
    arr.removeByValue(10);
    arr.print();           // [99, 40, 50]

    // 8. 测试升序排序
    arr.sort(true);
    arr.print();           // [40, 50, 99]

    // 9. 测试降序排序
    arr.sort(false);
    arr.print();           // [99, 50, 40]

    // 10. 测试越界访问
    System.out.println(arr.get(10));  // 下标越界,无法获取元素 → -1
    arr.remove(10);                   // 下标越界,无法删除元素 → -1

    // 11. 测试清空
    arr.clear();
    System.out.println("清空后是否为空:" + arr.isEmpty()); // true
    arr.print();           // []
}

} ```


挑战加分项(可选)

如果完成了基础版本,可以进一步挑战以下扩展功能:

  1. 泛型支持:将 MyArray 改造为支持任意类型的 MyArrayList<T>(使用泛型,存储 Object[]
  2. 缩容机制:当元素个数少于容量的 1/4 时,自动缩容为当前容量的一半(节省内存)
  3. getMax() / getMin():返回数组中的最大值和最小值
  4. addFirst(int element):在数组头部插入元素(所有元素后移)
  5. toArray():返回一个包含所有元素的新数组(长度 = size)
  6. subList(int fromIndex, int toIndex):返回从 fromIndextoIndex 的子数组视图

本题设计思路:这个练习是模拟 JDK 中 ArrayList 的底层实现。通过手写动态数组,你能深刻理解:

  • 构造方法如何初始化内部数据结构
  • 数组长度固定的局限性以及「扩容」机制的实现原理
  • 删除元素时「元素前移」的内存操作细节
  • 方法封装如何对外提供简洁的 API,隐藏内部实现复杂度
  • 这是通往「数据结构与算法」的第一级台阶 🪜

📋 题目总览

序号 所属部分 核心知识点 难度
练习 1 随堂 无参构造方法、对象创建
练习 2 随堂 有参构造方法、属性初始化
练习 3 随堂 构造方法重载(3 种形式) ⭐⭐
练习 4 随堂 构造方法 + this 关键字 ⭐⭐
练习 5 随堂 private 构造方法、单例模式雏形 ⭐⭐⭐
练习 6 随堂 默认构造方法被覆盖 ⭐⭐
进阶 1 进阶 无参/全参构造方法、封装
进阶 2 进阶 全参构造 + 对象数组 + 遍历 ⭐⭐
进阶 3 进阶 构造方法中参数校验、封装 ⭐⭐
进阶 4 进阶 构造方法重载 + 默认值 ⭐⭐
进阶 5 进阶 构造方法 + 方法封装 + 校验 ⭐⭐
进阶 6 进阶 this() 构造方法间调用 ⭐⭐⭐
进阶 7 进阶 全参构造 + 对象数组 + 遍历查找 ⭐⭐⭐
进阶 8 进阶 构造方法 + Random + Scanner + 循环 ⭐⭐⭐⭐
进阶 9 进阶 构造方法 + 封装 + 对象数组 + 排序 + 查找 ⭐⭐⭐⭐
进阶 10 进阶 综合大挑战(构造方法 + 封装 + 包 + 对象数组 + 排序 + 平均分) ⭐⭐⭐⭐⭐
进阶 11 进阶 MyArray 可变数组(构造方法 + 数组扩容 + 增删改查 + 元素前移 + 冒泡排序 + 方法封装) ⭐⭐⭐⭐⭐