说明:以下习题围绕今日授课核心内容——构造方法(构造器) 展开,涵盖构造方法的概念、格式、重载、
private构造方法以及构造方法与封装的综合应用。题目分为两部分:随堂练习(构造方法入门,6 题)和 进阶练习(综合挑战,10 题)。
以下练习围绕构造方法的基础概念和用法,每题独立可练。
难度:⭐
知识点:类定义、无参构造方法
创建一个 Cat 类,包含:
name(String)、age(int)"一只小猫出生了!",并将 name 赋值为 "小咪",age 赋值为 1测试类要求:创建 Cat 对象,通过 getter 输出 name 和 age 的值。
预期输出:
一只小猫出生了!
name = 小咪, age = 1
难度:⭐
知识点:有参构造方法
创建一个 Dog 类,包含:
name(String)、breed(品种,String)Dog(String name, String breed) —— 通过参数为属性赋值showInfo() —— 输出 "品种:xxx,名字:xxx"测试类要求:使用有参构造方法创建 2 个 Dog 对象,分别调用 showInfo() 输出信息。
示例输出:
品种:金毛,名字:旺财
品种:哈士奇,名字:二哈
难度:⭐⭐
知识点:构造方法重载
创建一个 Book 类,包含:
title(书名,String)、author(作者,String)、price(价格,double)"未知",作者 = "未知",价格 = 0.0Book(String title, String author) —— 书名和作者由参数传入,价格默认为 0.0Book(String title, String author, double price) —— 所有属性由参数传入showInfo() 输出所有属性信息测试类要求:分别使用 3 种构造方法创建 3 个 Book 对象,调用 showInfo() 输出。
示例输出:
书名:未知,作者:未知,价格:0.0
书名:Java从入门到精通,作者:张三,价格:0.0
书名:设计模式,作者:李四,价格:59.0
难度:⭐⭐
知识点:构造方法 + this 关键字
创建一个 Phone 类,包含:
brand(品牌,String)、price(价格,double)、color(颜色,String)Phone(String brand, double price, String color) —— 使用 this 关键字区分同名参数call(String name) —— 输出 "品牌xxx的手机正在给yyy打电话"测试类要求:使用全参构造创建对象,调用 call() 方法。
示例输出:
品牌华为的手机正在给张三打电话
难度:⭐⭐⭐
知识点:private 构造方法
创建一个 Manager 类,包含:
name(String)private Manager() —— 构造方法内部输出 "Manager 对象已创建"static 方法:public static Manager getInstance() —— 内部 new Manager() 并返回该对象测试类要求:
new Manager(),观察编译错误Manager.getInstance() 方法获取 Manager 对象,设置 name 并输出提示:这是单例设计模式的雏形——通过私有构造方法 + 静态方法控制对象的创建。
难度:⭐⭐
知识点:默认构造方法被覆盖
创建一个 StudentV3 类,包含:
name(String)、score(int)StudentV3(String name, int score)测试类要求:
new StudentV3() —— 观察编译是否报错(因为显式定义了构造方法,默认无参构造被覆盖)new StudentV3("小明", 90) 创建对象,通过 getter 输出信息思考:为什么 new StudentV3() 会报错?
以下为综合挑战题,融合构造方法与封装、对象数组、Random、方法等知识点,难度逐步递增。
创建一个 Computer 类,包含:
brand(品牌)、cpu(CPU 型号)、memory(内存大小,int,单位 GB)、price(价格)"未知品牌",CPU = "未知CPU",内存 = 8,价格 = 3999.0showConfig() 输出电脑配置信息测试类要求:分别使用无参构造和全参构造创建两个 Computer 对象,调用 showConfig() 输出。
定义一个 Hero(英雄)类,包含:
name(英雄名)、hp(血量,int)、attack(攻击力,int)Hero(String name, int hp, int attack)showInfo() 输出英雄信息测试类要求:
Hero 对象(数据自拟:如"盖伦" 580 100、"剑圣" 450 130 等)Hero[] 数组showInfo() 方法创建一个 BankAccount 类,包含:
accountNo(账号,String)、owner(户主,String)、balance(余额,double)BankAccount(String accountNo, String owner, double balance)balance 小于 0,则输出 "初始余额不能为负数,已设置为0.0",并将余额强制设为 0.0deposit(double amount) 存款(金额必须大于0),withdraw(double amount) 取款(不能透支)测试类要求:
创建一个 Employee 类,包含:
id(工号)、name(姓名)、position(职位)、salary(薪资)Employee(String id, String name, String position) —— 薪资默认为 5000.0Employee(String id, String name) —— 职位默认 "普通员工",薪资默认 3500.0work() 输出 "工号xxx的员工xxx正在工作,职位:xxx,薪资:xxx"测试类要求:使用 3 种构造方法分别创建对象,调用 work() 方法。
创建一个 Circle 类,包含:
radius(半径,double)1.0Circle(double radius)"半径必须大于0"getArea() 返回面积(π × r²)getPerimeter() 返回周长(2 × π × r)测试类要求:创建两个 Circle 对象(一个无参,一个有参),分别输出面积和周长。
提示:π 取
3.1415926,面积和周长的返回值为double类型。
注意:
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
创建一个 Movie(电影)类,包含:
title(片名)、director(导演)、year(上映年份,int)、rating(评分,double)showInfo() 输出电影信息测试类要求:
Movie[] 数组,存储至少 4 部电影示例数据:
"肖申克的救赎", "弗兰克·德拉邦特", 1994, 9.7
"霸王别姬", "陈凯歌", 1993, 9.6
"阿甘正传", "罗伯特·泽米吉斯", 1994, 9.5
"泰坦尼克号", "詹姆斯·卡梅隆", 1997, 9.4
将猜数字游戏改造为面向对象版本,使用构造方法初始化游戏:
GuessGame 类:
target(目标数字,int)、maxRange(最大范围,int)、attempts(已猜次数,int)GuessGame(int maxRange) —— 在 1 ~ maxRange 之间用 Random 生成目标数字guess(int num) —— 判断猜测结果,返回 String:"猜对了!" / "大了,再小点" / "小了,再大点"getAttempts() —— 返回已猜次数GuessGameTest 测试类:
GuessGame 对象,范围 1~100Scanner 让用户循环输入猜测数字guess() 方法判断,直到猜对为止创建一个 BookV2 类:
id(编号,int)、title(书名)、price(价格,double)创建一个 BookManager 类,包含以下方法:
initBooks() —— 创建并返回一个包含 5 本图书的 BookV2[] 数组(使用全参构造初始化)printBooks(BookV2[] books) —— 遍历输出所有图书信息sortByPrice(BookV2[] books) —— 按价格从高到低排序(冒泡排序)searchByTitle(BookV2[] books, String title) —— 根据书名查找图书,返回找到的图书对象(找不到返回 null)BookManagerTest 测试类:
initBooks() 初始化图书数组printBooks() 输出原始顺序sortByPrice() 排序后再次输出searchByTitle() 查找某本书,输出查找结果使用构造方法完成完整的学生成绩管理系统,在包 system 下创建以下类:
StudentV5 类(实体类):
id(学号,int)、name(姓名,String)、javaScore(Java 成绩,double)、mysqlScore(MySQL 成绩,double)getAverage() 返回两门课的平均分StudentSystem 类(业务类):
StudentV5[] students(学生数组)、count(当前学生数量)StudentSystem(int capacity) —— 初始化指定容量的学生数组addStudent(StudentV5 s) —— 将学生添加到数组中(如果数组已满,提示"数组已满,添加失败")printAll() —— 输出所有学生信息(含平均分)sortByAverage() —— 按平均分降序排序(冒泡排序)getTopStudent() —— 返回平均分最高的学生对象SystemTest 类(测试类):
StudentSystem 对象,容量为 5addStudent() 逐个添加学生printAll() 输出所有学生信息sortByAverage() 排序后再次输出getTopStudent() 输出最高分学生信息模仿
ArrayList的底层原理,自己动手实现一个可变数组(动态数组)。本题综合运用 构造方法、数组扩容、方法封装、排序算法 等核心知识点。
Java 中普通数组 int[] 一旦创建,长度就固定了,无法动态增减元素。请自行实现一个 可变整型数组 MyArray 类,内部使用 int[] 存储数据,对外提供便捷的增删改查方法。
私有属性:
| 属性 | 类型 | 说明 |
|---|---|---|
data |
int[] |
内部真正存储数据的数组 |
size |
int |
当前已存储的元素个数(不是数组长度) |
构造方法:
| 构造方法 | 说明 |
|---|---|
MyArray() |
无参构造,默认初始化容量为 10 的数组 |
MyArray(int capacity) |
指定初始容量,如果传入的容量 ≤ 0,则使用默认容量 10 |
提示:构造方法中只需
data = new int[capacity]; size = 0;,不要预填数据。
要求实现以下 11 个方法:
add(int element)将元素添加到数组的末尾。
size == data.length)data.length * 3 / 2)datadata[size],size 自增 1get(int index)返回指定下标处的元素值。
index < 0 || index >= size),输出 "下标越界,无法获取元素",返回 -1data[index]set(int index, int element)将指定下标处的元素修改为新值。
"下标越界,无法修改元素",不执行修改data[index] 赋值为 elementsize()返回当前已存储的元素个数(size 属性值)。
capacity()返回内部数组的总长度(data.length)。
isEmpty()如果 size == 0 返回 true,否则返回 false。
indexOf(int element)从前向后遍历数组,查找第一个值与 element 相等的元素。
-1contains(int element)调用 indexOf() 方法,根据返回值是否为 -1 来判断。
remove(int index)删除指定下标处的元素。
"下标越界,无法删除元素",返回 -1index 之后的所有元素向前移动一位(data[j] = data[j+1])size 自减 1示意图(删除下标 2 的元素):
删除前:[10, 20, 30, 40, 50] size=5
删除后:[10, 20, 40, 50, 0] size=4
removeByValue(int element)删除数组中第一个值与 element 相等的元素。
indexOf(element) 获取下标-1,输出 "未找到该元素,删除失败",返回 falseremove(index) 方法,返回 truesort(boolean ascending)对数组中的元素进行排序。
ascending 为 true 时升序排列(小→大),为 false 时降序排列(大→小)data[0] ~ data[size-1])排序,不要动后面的空闲位置print()遍历输出数组中的所有元素,格式为 [元素1, 元素2, 元素3, ...]。
[]clear()清空数组中的所有元素,将 size 置为 0(内部数组对象保留,不清除)。
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
}
}
在 main 方法中按顺序完成以下操作,并观察输出是否符合预期:
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()); // 6(3→4→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(); // []
}
}
如果完成了基础版本,可以进一步挑战以下扩展功能:
MyArray 改造为支持任意类型的 MyArrayList<T>(使用泛型,存储 Object[])getMax() / getMin():返回数组中的最大值和最小值addFirst(int element):在数组头部插入元素(所有元素后移)toArray():返回一个包含所有元素的新数组(长度 = size)subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 的子数组视图本题设计思路:这个练习是模拟 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 可变数组(构造方法 + 数组扩容 + 增删改查 + 元素前移 + 冒泡排序 + 方法封装) | ⭐⭐⭐⭐⭐ |