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

