本课作品
制作三种入口的玩家生成器
会写与类同名的构造器;能用 this 区分字段和参数;能在对象出生时完成校验;能用构造器重载提供三种合法出生方式。 前置背包:类、字段、实例方法、参数、重载和对象引用。今天要让每个对象从出生第一秒就处于合法状态。
开始前:建议先完成第 17–18 课
- 默认出生this("Steve")
- 指定名字this(name, 20)
- 完整构造器校验并写入字段
01 / 12
开始前:把示例放对地方
本章使用 JDK 21,不需要预览功能。每个普通示例都给出完整程序,按标注的文件名保存。多个示例各放在自己的文件夹,避免辅助类同名。
终端命令:把中文占位符替换成实际名称
javac -encoding UTF-8 示例文件名.java
java 示例类名类名区分大小写。完整挑战代码若分成多段,请按顺序接在同一个文件中,不要为续段另建类。运行核对只列程序输出;需要输入的地方另给输入序列。
02 / 12
构造器负责出生体检
先 new 再逐个写字段,容易漏掉名字或留下负生命。构造器把必要数据放进创建过程。
- new选择匹配的构造器
- 构造器初始化新对象
- 字段保存出生状态
- 验证保证创建完成时合法
L19Example01.java · 完整独立示例
class Player {
String name;
int health;
Player(String name, int health) {
this.name = name == null || name.isBlank() ? "Steve" : name.trim();
this.health = Math.max(1, Math.min(20, health));
}
Player(String name) { this(name, 20); }
Player() { this("Steve"); }
}
public class L19Example01 {
public static void main(String[] args) {
Player alex = new Player("Alex", 20);
System.out.println(alex.name + "," + alex.health);
}
}运行结果
Alex,2003 / 12
构造器语法地图
构造器写在类体中。new 后面的实参必须匹配某个构造器参数列表。
- 类名构造器必须同名
- 参数列表必须与实参匹配
- 返回类型构造器没有返回类型
- 无参显式声明构造器后不会自动补无参
L19Example02.java · 完整独立示例
class Mob {
String name; int health;
Mob(String name, int health) {
this.name = name; this.health = health;
}
}
public class L19Example02 {
public static void main(String[] args) {
Mob zombie = new Mob("小僵", 20);
System.out.println(zombie.name);
}
}运行结果
小僵04 / 12
this 指向当前对象
构造器常让参数名与字段名一致。此时短名字表示参数,this.字段 表示正在创建的对象字段。
- this.name当前对象字段
- name本次传入的参数
- 正确赋值字段接收参数值
- name=name参数赋给自己,字段没变
L19Example03.java · 完整独立示例
class Player {
String name;
Player(String name) { name = name; }
}
public class L19Example03 {
public static void main(String[] args) {
Player p = new Player("Alex");
System.out.println(p.name);
}
}运行结果
null05 / 12
出生时完成校验
构造器是守住对象合法状态的第一道门。先定义规则,再选择修正值或抛出异常。初学阶段可使用安全默认值。
- 坏名字替换为Steve
- 有效名字去除两端空白
- 生命修正到1到20
- 策略所有入口保持一致
L19Example04.java · 完整独立示例
class Player {
String name;
int health;
Player(String name, int health) {
this.name = name == null || name.isBlank() ? "Steve" : name.trim();
this.health = Math.max(1, Math.min(20, health));
}
Player(String name) { this(name, 20); }
Player() { this("Steve"); }
}
public class L19Example04 {
public static void main(String[] args) {
Player p = new Player("", -8);
System.out.println(p.name + "," + p.health);
}
}运行结果
Steve,106 / 12
构造器重载
构造器可以重载,为常用创建方式提供简洁入口。参数列表必须不同。
- Player()提供默认名字
- Player(name)提供默认生命
- 完整构造器校验后写入字段
L19Example05.java · 完整独立示例
class Player {
String name;
int health;
Player(String name, int health) {
this.name = name == null || name.isBlank() ? "Steve" : name.trim();
this.health = Math.max(1, Math.min(20, health));
}
Player(String name) { this(name, 20); }
Player() { this("Steve"); }
}
public class L19Example05 {
public static void main(String[] args) {
Player a = new Player("Alex", 12);
Player b = new Player("Builder");
Player c = new Player();
System.out.println(a.name + "," + a.health);
System.out.println(b.name + "," + b.health);
System.out.println(c.name + "," + c.health);
}
}运行结果
Alex,12
Builder,20
Steve,2007 / 12
this 构造器链
this(...) 调用本类另一个构造器,必须是构造器第一条语句。这样默认值和校验只维护一份。
- Player()提供默认名字
- Player(name)提供默认生命
- 完整构造器校验后写入字段
BadConstructorOrder.java · 故意错误示例
class Player {
Player(String name) { }
Player() {
System.out.println("出生");
this("Steve");
}
}
public class BadConstructorOrder {
public static void main(String[] args) { }
}08 / 12
合法状态与测试
设计中希望一直保持的规则称为不变条件。本课先保证 Player 刚创建完成时,名字不能空,出生时生命必须在1到20之间。
- 名字创建后非空
- 生命出生时1到20
- 坐标项目内-100到100
- 长期保证还需封装防止任意修改
| 测试 | 创建方式 | 预期 |
|---|---|---|
| 完整 | new Player("Alex", 12) | Alex 12 |
| 默认生命 | new Player("Alex") | Alex 20 |
| 全默认 | new Player() | Steve 20 |
| 坏名字 | new Player("", 8) | Steve 8 |
| 坏生命 | new Player("Alex", 99) | Alex 20 |
09 / 12
构造器故障诊所
读到“constructor cannot be applied”时,比较 new 的实参数量和类型,再查看构造器声明。
- void类名普通方法,不是构造器
- 多实参没有匹配入口
- this顺序Java21中必须首句
- 漏字段可能留下默认值
BadConstructorCall.java · 故意错误示例
class Player {
Player(String name) { }
}
public class BadConstructorCall {
public static void main(String[] args) {
Player p = new Player("Alex", 20, 3);
}
}L19Example08B.java · 完整独立示例
class Player {
String name;
void Player(String name) { this.name = name; }
}
public class L19Example08B {
public static void main(String[] args) {
Player p = new Player();
System.out.println(p.name);
}
}运行结果
null10 / 12
挑战 实现 三种合法出生
挑战 程序叫 PlayerSpawn。三个构造器分别支持完整出生、指定名字和完全默认。
- 完整玩家名字、生命与坐标
- 指定名字其余使用默认值
- 默认玩家Steve与默认状态
- 坏材料按统一策略修正
PlayerSpawn.java · 完整独立示例
public class PlayerSpawn {
static class Player {
String name;
int health;
int x;
int z;
Player(String name, int health, int x, int z) {
this.name = name == null || name.isBlank() ? "Steve" : name.trim();
this.health = Math.max(1, Math.min(20, health));
this.x = Math.max(-100, Math.min(100, x));
this.z = Math.max(-100, Math.min(100, z));
}
Player(String name) {
this(name, 20, 0, 0);
}
Player() {
this("Steve");
}
void describe() {
System.out.println(name + " 生命=" + health
+ " 出生点=(" + x + "," + z + ")");
}
}
public static void main(String[] args) {
Player a = new Player("Alex", 18, 12, -7);
Player b = new Player("Builder");
Player c = new Player();
Player d = new Player("", -5, 999, -999);
a.describe(); b.describe(); c.describe(); d.describe();
}
}运行结果
Alex 生命=18 出生点=(12,-7)
Builder 生命=20 出生点=(0,0)
Steve 生命=20 出生点=(0,0)
Steve 生命=1 出生点=(100,-100)11 / 12
拓展任务与通关复盘
作业需要为每个构造器写至少两个测试,并说明对象的不变条件。
- 木镐Mob增加构造器
- 铁镐重载与生命边界
- 钻石镐限制随机出生区域
- 复盘出生规则与this
三档作业
- 木镐:给第17课 Mob 加构造器,创建时必须提供名字和生命。
- 铁镐:增加重载构造器与参数校验,生命保持0到20。
- 钻石镐:设计随机出生坐标,但保证不落在指定危险范围。
| 自评 | 能 | 还要练 |
|---|---|---|
| 我能识别构造器 | □ | □ |
| 我会使用this | □ | □ |
| 我会测试出生规则 | □ | □ |
12 / 12
自己的验收记录
运行之前写预期,运行之后记录实际结果。正常案例说明程序会做事;边界案例说明它知道何时停;错误案例说明它能拒绝不合理的请求。
| 类别 | 你选择的输入 | 预期与实际 |
|---|---|---|
| 正常 | 一个常用值或一次成功操作 | 是否逐行一致? |
| 边界 | 最小值、最大值及相邻值 | 有没有少一格、多一步? |
| 错误 | 非法数量、缺失对象或错误语法 | 是否明确拒绝?状态是否保持? |
- 把一个 Bug 写成:触发条件、现象、原因、最小修复。
- 整理一个值得记住的规则,用自己的话解释,不能只复制定义。
- 改变一个示例,保留测试记录,让结果证明你的理解。
TAKE IT WITH YOU
下载本课 PDF
完整课文、代码、图解与练习,选择你需要的语言。