← Java 基础教程

第 17 / 40 课 · 免费教程

用 class 设计生物蓝图

类 字段与实例方法

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

本课作品

制作生物竞技场

会区分类和对象;能用字段保存每只生物的状态;能写实例方法让对象行动;能创建多个互不干扰的 Mob 并完成竞技场测试。 前置背包:变量、方法、参数、返回值和作用域。今天把散落的数据和动作搬进同一张蓝图。

开始前:建议先完成第 12–14 课

同一张蓝图,两个独立对象
  1. #1 · 僵尸对象health = 20
  2. #2 · 狼对象health = 16
变量引用指向
zombie#1
wolf#2

01 / 13

开始前:把示例放对地方

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

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

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

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

02 / 13

蓝图不是生物本身

如果每只僵尸都用一组散落变量表示,代码会像村庄袭击一样乱。类把相关数据和动作放进一个新类型。

class 像工作台配方 对象才是成品
  1. 类定义共同结构
  2. new创建具体对象
  3. 变量保存对象引用
  4. 状态实例字段分别保存

L17Example01.java · 完整独立示例

class Wolf { String name; int health; }
public class L17Example01 {
    public static void main(String[] args) {
        Wolf wolf = new Wolf();
        wolf.name = "小灰";
        wolf.health = 16;
        System.out.println(wolf.name);
    }
}

运行结果

小灰

四个问题

  • 类定义了什么共同结构?
  • new 创建了什么?
  • 对象变量保存的是什么?
  • 两只对象的字段会不会自动共享?

03 / 13

类的四块结构

类名通常使用大驼峰命名。字段写在类体内、方法外,实例方法可以直接访问同一个对象的字段。

类体把字段和方法围在花括号里
  1. class Mob声明新类型
  2. 字段名字与生命
  3. 实例方法操作当前对象
  4. 花括号类体容纳成员

L17Example02.java · 完整独立示例

class Mob {
    String name;
    int health;
    void describe() { System.out.println(name + " 生命=" + health); }
}
public class L17Example02 {
    public static void main(String[] args) {
        Mob mob = new Mob();
        mob.name = "小僵";
        mob.health = 20;
        mob.describe();
    }
}

运行结果

小僵 生命=20

04 / 13

字段保存对象状态

字段描述对象现在是什么样。使用点号选择具体对象的字段。

每个对象都有自己的状态箱
  1. 僵尸自己的名字与生命
  2. 狼另一份名字与生命
  3. 同一蓝图相同的字段结构
  4. 两次new得到独立对象

L17Example03.java · 完整独立示例

class Mob { String name; int health; }
public class L17Example03 {
    public static void main(String[] args) {
        Mob zombie = new Mob();
        zombie.name = "小僵"; zombie.health = 20;
        Mob wolf = new Mob();
        wolf.name = "小灰"; wolf.health = 16;
        System.out.println(zombie.health + wolf.health);
    }
}

运行结果

36

05 / 13

实例方法操作相关状态

把生物规则放进生物类,调用处就不必到处手动修改 health。

对象让自己的方法修改自己的字段
  1. takeDamage只接受正伤害
  2. health扣血但不低于0
  3. 实例调用点号左边选择对象
  4. 下一步封装限制直接修改

L17Example04.java · 完整独立示例

class Mob {
    String name;
    int health;
    void takeDamage(int n) {
        if (n > 0) health = Math.max(0, health - n);
    }
    int heal(int n) {
        if (n <= 0) return 0;
        int gained = Math.min(n, 20 - health);
        health += gained;
        return gained;
    }
    boolean isAlive() { return health > 0; }
    void describe() { System.out.println(name + " 生命=" + health); }
}
public class L17Example04 {
    public static void main(String[] args) {
        Mob zombie = new Mob(); zombie.health = 20;
        Mob wolf = new Mob(); wolf.health = 16;
        zombie.takeDamage(7);
        wolf.takeDamage(3);
        System.out.println(zombie.health + "," + wolf.health);
    }
}

运行结果

13,13

06 / 13

多个对象互不干扰

创建三个 Mob,再分别受伤和治疗。观察同一方法在不同对象上产生不同结果。

同一蓝图生成一支对象军团
  1. 对象a受到5点伤害
  2. 对象b没有受到伤害
  3. 对象c生命降到0
  4. 结论一次调用只操作目标对象

L17Example05.java · 完整独立示例

class Mob {
    String name;
    int health;
    void takeDamage(int n) {
        if (n > 0) health = Math.max(0, health - n);
    }
    int heal(int n) {
        if (n <= 0) return 0;
        int gained = Math.min(n, 20 - health);
        health += gained;
        return gained;
    }
    boolean isAlive() { return health > 0; }
    void describe() { System.out.println(name + " 生命=" + health); }
}
public class L17Example05 {
    public static void main(String[] args) {
        Mob a = new Mob(); a.health = 20;
        Mob b = new Mob(); b.health = 20;
        Mob c = new Mob(); c.health = 16;
        a.takeDamage(5);
        c.takeDamage(20);
        System.out.println(a.health + "," + b.health + "," + c.health);
    }
}

运行结果

15,20,0

07 / 13

方法参数与返回值

实例方法和 static 方法一样可以有参数与返回值。区别是它还自动连接到当前对象。

实例方法也能收材料并交回结果
  1. heal参数请求增加量
  2. 实际增加不超过剩余空间
  3. 返回值告诉调用者实际增加量
  4. isAlive只回答当前是否存活

L17Example06.java · 完整独立示例

class Mob {
    String name;
    int health;
    void takeDamage(int n) {
        if (n > 0) health = Math.max(0, health - n);
    }
    int heal(int n) {
        if (n <= 0) return 0;
        int gained = Math.min(n, 20 - health);
        health += gained;
        return gained;
    }
    boolean isAlive() { return health > 0; }
    void describe() { System.out.println(name + " 生命=" + health); }
}
public class L17Example06 {
    public static void main(String[] args) {
        Mob wolf = new Mob(); wolf.health = 16;
        System.out.println(wolf.heal(10));
        System.out.println(wolf.health);
        System.out.println(wolf.isAlive());
    }
}

运行结果

4
20
true

08 / 13

设计 Mob 的责任地图

好类不是把所有代码塞进新花括号。字段和方法应围绕同一种对象的责任。

状态和行为要围绕同一个对象
  1. name保存对象名字
  2. health保存当前生命
  3. 规则方法受伤、治疗与查询
  4. 边界不让单只生物管理全世界
验收表
成员类型责任
nameString 字段保存名字
healthint 字段保存当前生命
takeDamage实例方法扣血并守住下限
heal实例方法回血并守住上限
isAlive实例方法回答是否存活

09 / 13

类与对象故障诊所

最常见错误是把类名当对象、在 static main 中直接访问实例字段,或声明变量后忘记创建对象。

static 和实例是两条不同轨道
  1. 类名访问实例字段需要对象
  2. 只声明局部引用仍未初始化
  3. 错误static可能意外共享状态
  4. 检查从new与点号左边开始

BadInstance.java · 故意错误示例

class Mob { int health; }
public class BadInstance {
    public static void main(String[] args) { Mob.health = 10; }
}

BadObjectInit.java · 故意错误示例

class Mob { int health; }
public class BadObjectInit {
    public static void main(String[] args) {
        Mob mob;
        mob.health = 20;
    }
}

10 / 13

挑战 蓝图 Mob 竞技场

挑战 程序叫 MobArena。Mob 保存名字、生命和攻击力;attack 让目标受伤,describe 显示当前状态。

两只生物用自己的方法完成战斗
  1. 创建两只独立生物
  2. 攻击调用目标受伤方法
  3. 存活检查倒下者不再参与
  4. 测试伤害恰好等于生命

验收条件

  • 创建僵尸和铁傀儡两个对象。
  • 双方轮流攻击,生命不低于0。
  • 攻击已倒下目标时不继续扣血。
  • 输出能看出两个对象状态互不干扰。
验收表
测试操作预期
正常僵尸攻击傀儡傀儡生命减少
边界伤害等于剩余生命生命变0
错误负数攻击力按0处理
状态修改一只生命另一只不变

11 / 13

挑战 实现 Mob 竞技场

新建 MobArena.java。内部 Mob 类用于本课演示,运行后检查每轮战斗输出。

类把状态和行为收进同一张蓝图
  1. 状态名字、生命、攻击力
  2. 行为受伤、攻击、描述
  3. main安排战斗顺序
  4. 结果两只对象生命分别变化

MobArena.java · 第1/2段,顺序接在同一文件

public class MobArena {
    static class Mob {
        String name;
        int health;
        int attackPower;

        void takeDamage(int amount) {
            health = Math.max(0, health - Math.max(0, amount));
        }
        void attack(Mob target) {
            if (target != null && isAlive() && target.isAlive()) target.takeDamage(attackPower);
        }
        boolean isAlive() { return health > 0; }
        void describe() {
            System.out.println(name + " 生命=" + health);
        }
    }

    static Mob create(String name, int health, int power) {
        Mob mob = new Mob();
        mob.name = name;
        mob.health = Math.max(0, health);
        mob.attackPower = Math.max(0, power);
        return mob;
    }

    public static void main(String[] args) {
        Mob zombie = create("小僵", 20, 5);

MobArena.java · 第2/2段,顺序接在同一文件

        Mob golem = create("铁傀儡", 30, 12);
        zombie.attack(golem);
        golem.attack(zombie);
        zombie.describe();
        golem.describe();
        golem.attack(zombie);
        zombie.describe();
        golem.describe();
    }
}

运行结果

小僵 生命=8
铁傀儡 生命=25
小僵 生命=0
铁傀儡 生命=25

12 / 13

拓展任务与通关复盘

作业需提交类图草稿、源代码、运行结果和一个故障记录。

三档任务扩建生物设计院
  1. 木镐设计两只羊
  2. 铁镐双对象轮流攻击
  3. 钻石镐宠物信任与行为
  4. 复盘类、对象与实例字段

三档作业

  • 木镐:设计 Sheep 类,包含颜色、生命和叫声方法,创建两只羊。
  • 铁镐:让两个 Mob 互相攻击,记录每轮生命。
  • 钻石镐:设计可驯服 Pet 类,包含信任值、喂食和跟随行为。
验收表
自评能还要练
我能区分类与对象□□
我会写实例字段□□
我会让方法维护状态□□

13 / 13

自己的验收记录

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

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

TAKE IT WITH YOU

下载本课 PDF

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