You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java RPG游戏代码NullPointerException问题排查与优化咨询

问题修复与代码优化指导

嘿,我来帮你一步步解决这个问题!首先搞定那个烦人的NullPointerException,然后再聊聊用Setter/Getter优化代码,以及如何让你的RPG对战代码更健壮。


一、先解决NullPointerException错误

这个报错的根源有两个核心问题:

1. Main类中装备武器的代码逻辑错误

看你Main.java里这段代码:

Mensch.equip(Mensch.weapon);
Ork.equip(Ork.weapon);

你这里把Player自己还未初始化的weapon属性传进去了(默认是null),正确的做法应该是把你提前创建好的MenschW和OrkW传进去:

Mensch.equip(MenschW);
Ork.equip(OrkW);

2. Weapon类构造函数未初始化成员变量

你的Weapon构造函数是空的,导致创建Weapon对象时,name、damage、hp都没有被赋值(默认是null或0),后续调用useWeapon()也会引发问题。修改构造函数:

public Weapon(String string, int d, int hp) {
    this.name = string;
    this.damage = d;
    this.hp = hp;
}

修复这两个问题后,NullPointerException就会消失了。


二、使用Setter/Getter优化代码

你的代码已经有Setter/Getter,但可以进一步规范封装,让代码更安全:

1. 私有化所有成员变量

现在你的Player和Weapon类的成员变量都是默认的包级访问权限,应该改成private,强制通过Setter/Getter访问,保证封装性:
比如Player类的成员:

private String name;
private String race;
private int hp;
private int power;
private int armour;
private Weapon weapon;

Weapon类同理,把所有成员变量改成private。

2. 在Setter中增加参数校验

比如设置hp的时候,不能是负数,在Setter里做判断:

public void setHP(int h) {
    this.hp = Math.max(h, 0); // 确保生命值不会变成负数
}

Weapon的setDamage和setWHP也可以加类似校验:

public void setDamage(int d) {
    this.damage = Math.max(d, 0); // 保证伤害值非负
}

3. 用Getter替代直接访问成员变量

在类内部的方法里,尽量用Getter获取属性值,比如receiveDamage方法:

public boolean receiveDamage(int i) {
    int newHp = getHP() - i;
    setHP(Math.max(newHp, 0)); // 直接复用Setter的校验逻辑
    return getHP() > 0;
}

这样可以避免重复写校验逻辑,也让代码更统一。


三、提升代码健壮性的建议

1. 处理武器未装备的情况

在Player的attack方法里,如果weapon还是null(比如忘记调用equip),还是会抛NPE,所以要加判断:

public boolean attack(Player player) {
    if (weapon == null) {
        // 没有武器时用基础攻击力
        return player.receiveDamage(this.power);
    }
    return player.receiveDamage(weapon.useWeapon());
}

或者也可以在Player构造函数里给weapon一个默认的“徒手”武器,从根源避免null。

2. 增加参数合法性校验

比如Player的构造函数,参数不能是无效值:

public Player(String n, String r, int h, int p, int a) {
    if (n == null || n.isEmpty()) {
        throw new IllegalArgumentException("名字不能为空");
    }
    if (r == null || r.isEmpty()) {
        throw new IllegalArgumentException("种族不能为空");
    }
    this.name = n;
    this.race = r;
    this.hp = Math.max(h, 0);
    this.power = Math.max(p, 0);
    this.armour = Math.max(a, 0);
}

这样创建Player对象时就不会出现无效属性,提前发现错误。

3. 优化伤害计算逻辑

现在的receiveDamage没考虑护甲,你可以加入护甲减免逻辑,更符合RPG游戏设定:

public boolean receiveDamage(int damage) {
    // 护甲减免部分伤害,至少保留1点伤害避免无效攻击
    int actualDamage = Math.max(damage - getArmour(), 1);
    setHP(getHP() - actualDamage);
    return getHP() > 0;
}

4. 封装对战逻辑

把Main里的对战循环封装成独立方法,让代码更清晰:

public static void startBattle(Player p1, Player p2) {
    System.out.println("=== 战斗开始 ===");
    while (!p1.dead() && !p2.dead()) {
        p1.attack(p2);
        System.out.printf("%s 攻击了 %s,%s 当前HP:%d%n", 
                p1.getName(), p2.getName(), p2.getName(), p2.getHP());
        if (p2.dead()) break;
        
        p2.attack(p1);
        System.out.printf("%s 攻击了 %s,%s 当前HP:%d%n", 
                p2.getName(), p1.getName(), p1.getName(), p1.getHP());
    }
    System.out.println("=== 战斗结束 ===");
    System.out.println(p1.getName() + " 是否死亡:" + p1.dead());
    System.out.println(p2.getName() + " 是否死亡:" + p2.dead());
}

然后Main里直接调用startBattle(Mensch, Ork)即可。


四、修复后的完整代码示例

Player.java

public class Player {
    private String name;
    private String race;
    private int hp;
    private int power;
    private int armour;
    private Weapon weapon;

    public Player(String n, String r, int h, int p, int a) {
        if (n == null || n.isEmpty()) {
            throw new IllegalArgumentException("名字不能为空");
        }
        if (r == null || r.isEmpty()) {
            throw new IllegalArgumentException("种族不能为空");
        }
        this.name = n;
        this.race = r;
        this.hp = Math.max(h, 0);
        this.power = Math.max(p, 0);
        this.armour = Math.max(a, 0);
    }

    public void setName(String n) {
        if (n != null && !n.isEmpty()) {
            this.name = n;
        }
    }

    public String getName() {
        return name;
    }

    public void setRace(String r) {
        if (r != null && !r.isEmpty()) {
            this.race = r;
        }
    }

    public String getRace() {
        return race;
    }

    public void setHP(int h) {
        this.hp = Math.max(h, 0);
    }

    public int getHP() {
        return hp;
    }

    public void setPower(int p) {
        this.power = Math.max(p, 0);
    }

    public int getPower() {
        return power;
    }

    public void setArmour(int a) {
        this.armour = Math.max(a, 0);
    }

    public int getArmour() {
        return armour;
    }

    public boolean dead() {
        return getHP() <= 0;
    }

    public boolean equip(Weapon weapon) {
        this.weapon = weapon;
        return true;
    }

    public boolean receiveDamage(int damage) {
        int actualDamage = Math.max(damage - getArmour(), 1);
        setHP(getHP() - actualDamage);
        return getHP() > 0;
    }

    public boolean attack(Player player) {
        if (player == null) {
            throw new IllegalArgumentException("攻击目标不能为空");
        }
        int damage = getPower();
        if (weapon != null) {
            damage = weapon.useWeapon();
        }
        return player.receiveDamage(damage);
    }
}

Weapon.java

import java.util.concurrent.ThreadLocalRandom;

public class Weapon {
    private String name;
    private int damage;
    private int hp;

    public Weapon(String string, int d, int hp) {
        if (string == null || string.isEmpty()) {
            throw new IllegalArgumentException("武器名字不能为空");
        }
        this.name = string;
        this.damage = Math.max(d, 0);
        this.hp = Math.max(hp, 0);
    }

    public void setName(String n) {
        if (n != null && !n.isEmpty()) {
            this.name = n;
        }
    }

    public String getName() {
        return name;
    }

    public void setDamage(int d) {
        this.damage = Math.max(d, 0);
    }

    public int getDamage() {
        return damage;
    }

    public void setWHP(int h) {
        this.hp = Math.max(h, 0);
    }

    public int getWHP() {
        return hp;
    }

    public int useWeapon() {
        if (broken()) return 0;
        setWHP(getWHP() - 5);
        return (getDamage() / 2) + random();
    }

    private int random() {
        return ThreadLocalRandom.current().nextInt(1, getDamage() + 1);
    }

    private boolean broken() {
        return getWHP() <= 0;
    }
}

Main.java

public class Main {
    public static void main(String args[]) {
        Player mensch = new Player("Mensch", "Mensch", 85, 12, 10);
        Player ork = new Player("Shrek", "Ork", 50, 14, 6);
        Weapon menschW = new Weapon("mächtiges Schwert", 15, 100);
        Weapon orkW = new Weapon("große Axt", 7, 100);

        mensch.equip(menschW);
        ork.equip(orkW);

        startBattle(mensch, ork);
    }

    public static void startBattle(Player p1, Player p2) {
        System.out.println("=== 战斗开始 ===");
        while (!p1.dead() && !p2.dead()) {
            p1.attack(p2);
            System.out.printf("%s 攻击了 %s,%s 当前HP:%d%n", 
                    p1.getName(), p2.getName(), p2.getName(), p2.getHP());
            if (p2.dead()) break;
            
            p2.attack(p1);
            System.out.printf("%s 攻击了 %s,%s 当前HP:%d%n", 
                    p2.getName(), p1.getName(), p1.getName(), p1.getHP());
        }
        System.out.println("=== 战斗结束 ===");
        System.out.println(p1.getName() + " 是否死亡:" + p1.dead());
        System.out.println(p2.getName() + " 是否死亡:" + p2.dead());
    }
}

内容的提问来源于stack exchange,提问作者Niko Abidus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:51:57