Java中如何在两个相似类间共享对象?以游戏玩家共享牌组为例
嘿,这个问题问到点子上了!看你用了ArrayList,应该是用Java开发游戏吧?其实你现在的场景里,根本不用特意去“返回修改后的cardList”——因为Java里对象是按引用传递的,只要Human和Computer拿到的是同一个Deck(或者cardList)的引用,它们操作的就是同一个实例,修改会自动同步的!
下面给你两种可行的实现思路,推荐第一种,更符合面向对象的设计原则:
别直接把cardList暴露给玩家类,而是把抽牌、洗牌这些和牌堆相关的逻辑都封装在Deck类里,然后让Human和Computer共享同一个Deck实例就行。这样既保证了数据的一致性,又避免了外部类直接操作内部列表的风险。
示例代码大概是这样的:
// Deck类:封装牌堆的所有逻辑 public class Deck { private ArrayList<Card> cardList; public Deck() { cardList = new ArrayList<>(); // 这里初始化所有卡牌,比如循环添加52张牌 for (String suit : new String[]{"红桃", "方块", "黑桃", "梅花"}) { for (int num = 1; num <= 13; num++) { cardList.add(new Card(suit, num)); } } } // 抽牌方法:内部负责修改cardList,返回抽到的牌 public Card drawCard() { if (cardList.isEmpty()) { throw new IllegalStateException("牌堆已经空啦!"); } // 从牌堆顶部抽牌(你也可以改成随机抽,只要在Deck里改就行) return cardList.remove(0); } // 可以加洗牌方法,同样封装在Deck里 public void shuffle() { Collections.shuffle(cardList); } } // Human类:依赖共享的Deck实例 public class Human { private ArrayList<Card> hand; private Deck sharedDeck; public Human(Deck deck) { this.sharedDeck = deck; this.hand = new ArrayList<>(); } public void takeCard() { Card drawnCard = sharedDeck.drawCard(); hand.add(drawnCard); System.out.println("人类玩家抽到了:" + drawnCard); } } // Computer类:和Human逻辑一致,共享同一个Deck public class Computer { private ArrayList<Card> hand; private Deck sharedDeck; public Computer(Deck deck) { this.sharedDeck = deck; this.hand = new ArrayList<>(); } public void takeCard() { Card drawnCard = sharedDeck.drawCard(); hand.add(drawnCard); System.out.println("电脑玩家抽到了:" + drawnCard); } } // 游戏启动时的代码 public class Game { public static void main(String[] args) { Deck gameDeck = new Deck(); gameDeck.shuffle(); // 先洗牌 Human player1 = new Human(gameDeck); Computer aiPlayer = new Computer(gameDeck); // 双方抽牌,操作的是同一个Deck里的cardList player1.takeCard(); aiPlayer.takeCard(); } }
这种方式下,不管是人类玩家还是电脑玩家抽牌,都是调用同一个Deck实例的drawCard方法,cardList的修改会自动同步给所有持有该Deck引用的对象,完全不需要手动返回或者传递修改后的列表。
如果你一定要直接传递cardList,那要记住:Java中ArrayList是对象,当你把它作为构造函数参数传给Human和Computer时,两个类拿到的是同一个列表的引用。也就是说,只要其中一方修改了列表(比如移除元素),另一方访问这个列表时看到的就是修改后的版本。
比如:
// Human类里的抽牌逻辑 public void drawCard(ArrayList<Card> cardList) { Card card = cardList.remove(0); this.hand.add(card); }
当你把同一个cardList传给Human和Computer后,不管哪一方调用这个方法,都会直接修改原列表,另一方再访问时就能看到最新状态。
但这种方式不推荐,因为它把Deck的内部数据暴露给了外部类,违反了封装原则——以后如果要改抽牌规则(比如从牌堆底部抽,或者添加弃牌堆逻辑),你得修改所有玩家类的代码,维护成本很高。
如果你的游戏是多线程的(比如人类玩家和电脑玩家在不同线程同时抽牌),直接操作共享的cardList会有并发冲突的风险。这时候可以给Deck的drawCard方法加上synchronized修饰符,保证同一时间只有一个线程能修改牌堆:
public synchronized Card drawCard() { // 原逻辑不变 }
内容的提问来源于stack exchange,提问作者Tula Malek

