← Java 基础教程

第 19 / 40 课 · 免费教程

生成点:构造方法

构造器 this 与初始化

90–110 分钟本章使用 JDK 21,无需预览功能。面向对象入门

本课作品

制作三种入口的玩家生成器

会写与类同名的构造器;能用 this 区分字段和参数;能在对象出生时完成校验;能用构造器重载提供三种合法出生方式。 前置背包:类、字段、实例方法、参数、重载和对象引用。今天要让每个对象从出生第一秒就处于合法状态。

开始前:建议先完成第 17–18 课

三个入口,一条初始化路线
  1. 默认出生this("Steve")
  2. 指定名字this(name, 20)
  3. 完整构造器校验并写入字段

01 / 12

开始前:把示例放对地方

本章使用 JDK 21,不需要预览功能。每个普通示例都给出完整程序,按标注的文件名保存。多个示例各放在自己的文件夹,避免辅助类同名。

终端命令:把中文占位符替换成实际名称

javac -encoding UTF-8 示例文件名.java
java 示例类名

类名区分大小写。完整挑战代码若分成多段,请按顺序接在同一个文件中,不要为续段另建类。运行核对只列程序输出;需要输入的地方另给输入序列。

02 / 12

构造器负责出生体检

先 new 再逐个写字段,容易漏掉名字或留下负生命。构造器把必要数据放进创建过程。

new 调用构造器创建并初始化对象
  1. new选择匹配的构造器
  2. 构造器初始化新对象
  3. 字段保存出生状态
  4. 验证保证创建完成时合法

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,20

03 / 12

构造器语法地图

构造器写在类体中。new 后面的实参必须匹配某个构造器参数列表。

类名 参数和构造器体缺一不可
  1. 类名构造器必须同名
  2. 参数列表必须与实参匹配
  3. 返回类型构造器没有返回类型
  4. 无参显式声明构造器后不会自动补无参

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 打开对象箱子
  1. this.name当前对象字段
  2. name本次传入的参数
  3. 正确赋值字段接收参数值
  4. 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);
    }
}

运行结果

null

05 / 12

出生时完成校验

构造器是守住对象合法状态的第一道门。先定义规则,再选择修正值或抛出异常。初学阶段可使用安全默认值。

非法材料在构造器入口被修正或拒绝
  1. 坏名字替换为Steve
  2. 有效名字去除两端空白
  3. 生命修正到1到20
  4. 策略所有入口保持一致

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,1

06 / 12

构造器重载

构造器可以重载,为常用创建方式提供简洁入口。参数列表必须不同。

构造器委托:最终只保留一份校验
  1. Player()提供默认名字
  2. Player(name)提供默认生命
  3. 完整构造器校验后写入字段

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,20

07 / 12

this 构造器链

this(...) 调用本类另一个构造器,必须是构造器第一条语句。这样默认值和校验只维护一份。

构造器委托:最终只保留一份校验
  1. Player()提供默认名字
  2. Player(name)提供默认生命
  3. 完整构造器校验后写入字段

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. 名字创建后非空
  2. 生命出生时1到20
  3. 坐标项目内-100到100
  4. 长期保证还需封装防止任意修改
验收表
测试创建方式预期
完整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 的实参数量和类型,再查看构造器声明。

编译错误通常来自名字 参数或 this 顺序
  1. void类名普通方法,不是构造器
  2. 多实参没有匹配入口
  3. this顺序Java21中必须首句
  4. 漏字段可能留下默认值

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);
    }
}

运行结果

null

10 / 12

挑战 实现 三种合法出生

挑战 程序叫 PlayerSpawn。三个构造器分别支持完整出生、指定名字和完全默认。

构造器链把所有出生方式送进同一套校验
  1. 完整玩家名字、生命与坐标
  2. 指定名字其余使用默认值
  3. 默认玩家Steve与默认状态
  4. 坏材料按统一策略修正

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

拓展任务与通关复盘

作业需要为每个构造器写至少两个测试,并说明对象的不变条件。

三档任务扩建生成点
  1. 木镐Mob增加构造器
  2. 铁镐重载与生命边界
  3. 钻石镐限制随机出生区域
  4. 复盘出生规则与this

三档作业

  • 木镐:给第17课 Mob 加构造器,创建时必须提供名字和生命。
  • 铁镐:增加重载构造器与参数校验,生命保持0到20。
  • 钻石镐:设计随机出生坐标,但保证不落在指定危险范围。
验收表
自评能还要练
我能识别构造器□□
我会使用this□□
我会测试出生规则□□

12 / 12

自己的验收记录

运行之前写预期,运行之后记录实际结果。正常案例说明程序会做事;边界案例说明它知道何时停;错误案例说明它能拒绝不合理的请求。

把证据写进记录
类别你选择的输入预期与实际
正常一个常用值或一次成功操作是否逐行一致?
边界最小值、最大值及相邻值有没有少一格、多一步?
错误非法数量、缺失对象或错误语法是否明确拒绝?状态是否保持?
  • 把一个 Bug 写成:触发条件、现象、原因、最小修复。
  • 整理一个值得记住的规则,用自己的话解释,不能只复制定义。
  • 改变一个示例,保留测试记录,让结果证明你的理解。

TAKE IT WITH YOU

下载本课 PDF

完整课文、代码、图解与练习,选择你需要的语言。