如何在 Java 中跨类访问对象实例并共享状态

本文讲解如何通过构造函数传递对象集合来实现跨类数据共享,避免静态引用或全局变量,确保 pokemon 战斗次数等状态可在 battle 和 catch 类间安全访问。

在 Java 面向对象开发中,直接通过类名访问实例(如 Battle.pikachu)是无效的——因为 Battle 是一个类,不是单例对象,其内部声明的 fire、water 等字段属于实例成员,必须先创建 Battle 对象才能访问。更关键的是,将游戏核心数据(如 Pokemon 实例)硬编码在 GUI 类(Battle extends JFrame)中,会严重破坏职责分离,导致数据无法被 CatchPokemon 等其他业务类复用。

✅ 正确做法是:将 Pokemon 实例的创建与管理权上移至主控制类(如 Main),再通过构造函数注入到需要它的类中。这样既保证了单一数据源,又支持多处共享和状态同步。

以下是一个结构清晰、可扩展的实现方案:

1. 完善 Pokemon 类(支持战斗计数)

public class Pokemon {
    private String name;
    private String type;
    private int defeatCount; // 新增:记录被击败次数

    public Pokemon(String name, String type) {
        this.name = name;
        this.type = type;
        this.defeatCount = 0;
    }

    // 提供安全的访问与修改方法
    public void incrementDefeat() {
        this.defeatCount++;
    }

    public int getDefeatCount() {
        return defeatCount;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return String.format("%s (%s) — Defeated %d time(s)", name, type, defeatCount);
    }
}

2. 在 Main 类中集中管理 Pokemon 实例

import javax.swing.*;
import java.util.ArrayList;

public class Main {
    private ArrayList pokemonList;

    public Main() {
        initializePokemon();
        launchGame();
    }

    private void initializePokemon() {
        pokemonList = new ArrayList<>();
        pokemonList.add(new Pokemon("Charmander", "Fire"));
        pokemonList.add(new Pokemon("Squirtle", "Water"));
        pokemonList.add(new Pokemon("Bulbasaur", "Grass"));
        pokemonList.add(new Pokemon("X", "Ground"));
    }

    private void launchGame() {
        // 启动 Battle 界面,并传入共享的 Pokemon 列表
        SwingUtilities.invokeLater(() -> {
            new Battle(pokemonList).setVisible(true);
        });
    }

    // 提供给 CatchPokemon 等其他模块访问同一份数据
    public ArrayList getPokemonList() {
        return pokemonList;
    }

    public static void main(String[] args) {
        new Main();
    }
}

3. Battle 类接收并操作共享列表

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class Battle extends JFrame {
    private ArrayList pokemonList;

    // 构造函数接收外部传入的 Pokemon 列表
    public Battle(ArrayList pokemonList) {
        this.pokemonList = pokemonList;
        setTitle("Pokemon Battle");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 300);

        // 示例:模拟击败 Charmander 一次
        for (Pokemon p : pokemonList) {
            if ("Charmander".equals(p.getName())) {
                p.incrementDefeat(); // 状态变更立即反映到所有持有该引用的地方
                System.out.println("✓ " + p); // 输出:Charmander (Fire) — Defeated 1 time(s)
                break;
            }
        }
    }
}

4. CatchPokemon 类同样可复用同一列表

public class CatchPokemon {
    private ArrayList pokemonList;

    public CatchPokemon(ArrayList pokemonList) {
        this.pokemonList = pokemonList;
    }

    public boolean canCatch(String pokemonName, int minDefeats) {
        for (Pokemon p : pokemonList) {
            if (p.getName().equals(pokemo

nName) && p.getDefeatCount() >= minDefeats) { return true; } } return false; } }

? 关键注意事项

  • ✅ 所有类共享的是 Pokemon 对象的引用,而非副本,因此 defeatCount 的修改对整个程序可见;
  • ❌ 避免使用 static 字段存储 Pokemon 实例(如 public static Pokemon fire = ...),这会阻碍单元测试、多存档支持,并违反面向对象封装原则;
  • ⚠️ 若需线程安全(如后台异步战斗逻辑),应对 defeatCount 的读写加锁或使用 AtomicInteger;
  • ? 进阶建议:将 pokemonList 封装进 PokemonManager 单例或依赖注入容器,便于统一管理持久化、事件通知等。

通过这种“中心化创建 + 构造注入”的模式,你不仅能解决当前的跨类访问问题,还为后续添加进化系统、图鉴统计、存档功能打下坚实基础。