创建两个完全相同但独立的扑克牌组并同时从两组抽牌
解决两个独立扑克牌组的抽牌问题
嘿,我一眼就看出你大概率踩了静态变量的坑!你说创建两个Deck实例后,抽104次只拿到一个牌组的52张牌,另一个全是空,几乎可以肯定是你的Deck类里用来存牌的数组/列表被定义成了static——静态变量是属于类的,不是属于单个实例的,所以两个Deck对象其实共用同一堆牌,第一个抽完52张后,第二个自然就没牌可抽了。
下面给你一套完整的修正方案,从Deck类的正确实现,到测试类的写法,帮你搞定两个独立牌组的并排抽牌:
1. 正确实现独立的Deck类
首先要确保牌组的核心存储(比如cards列表)是实例变量,不是静态的。同时要实现完整的创建、洗牌、抽牌逻辑:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Deck { // 每个Deck实例都有自己的cards列表,绝对不要加static! private List<String> cards; public Deck() { initStandardDeck(); shuffleDeck(); } // 初始化52张标准扑克牌 private void initStandardDeck() { cards = new ArrayList<>(); String[] suits = {"♠", "♥", "♦", "♣"}; String[] ranks = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}; for (String suit : suits) { for (String rank : ranks) { cards.add(rank + suit); } } } // 洗牌逻辑 public void shuffleDeck() { Collections.shuffle(cards); } // 抽牌:取出并移除最后一张牌,空牌组返回null(也可以抛出异常,按需调整) public String drawCard() { if (cards.isEmpty()) { return null; } // Java 9+用removeLast(),低版本可以用remove(cards.size()-1) return cards.removeLast(); } // 可选:获取剩余牌数,方便调试 public int getRemainingCards() { return cards.size(); } }
2. 测试类DeckDrawTest的正确写法
现在创建两个独立的Deck实例,循环抽104次,就能分别拿到两个牌组的所有牌了:
public class DeckDrawTest { public static void main(String[] args) { Deck deck1 = new Deck(); Deck deck2 = new Deck(); int totalDraws = 104; for (int i = 0; i < totalDraws; i++) { String cardFromDeck1 = deck1.drawCard(); String cardFromDeck2 = deck2.drawCard(); System.out.printf("第%d次抽牌:牌组1 = %s,牌组2 = %s%n", i+1, cardFromDeck1 != null ? cardFromDeck1 : "无牌", cardFromDeck2 != null ? cardFromDeck2 : "无牌"); } // 最后验证剩余牌数,应该都是0 System.out.println("牌组1剩余牌数:" + deck1.getRemainingCards()); System.out.println("牌组2剩余牌数:" + deck2.getRemainingCards()); } }
3. 如果需要复制现有牌组(比如复制一个完全相同的牌组)
如果你的需求是先创建一个牌组,然后复制出另一个初始状态完全一致的(比如牌的顺序、洗牌状态都相同),可以给Deck类加复制构造方法,或者实现Cloneable接口:
方案一:复制构造方法
在Deck类里添加:
// 复制构造方法,深拷贝传入的牌组的牌列表 public Deck(Deck otherDeck) { // 必须new一个新的ArrayList,避免两个牌组共享同一列表引用 this.cards = new ArrayList<>(otherDeck.cards); }
使用方式:
Deck originalDeck = new Deck(); Deck copiedDeck = new Deck(originalDeck); // 复制出完全相同的独立牌组
方案二:实现Cloneable接口
@Override public Deck clone() { try { Deck clonedDeck = (Deck) super.clone(); // 对引用类型成员做深拷贝 clonedDeck.cards = new ArrayList<>(this.cards); return clonedDeck; } catch (CloneNotSupportedException e) { throw new AssertionError(); // 不会触发,因为我们实现了Cloneable } }
使用方式:
Deck originalDeck = new Deck(); Deck clonedDeck = originalDeck.clone();
关键注意点
- 禁用静态变量存牌组:这是你之前遇到空值问题的核心原因,静态变量属于类,所有实例共享同一数据。
- 深拷贝的必要性:复制牌组时一定要复制底层列表,不能直接赋值引用,否则两个牌组还是会互相影响。
- 边界处理:抽牌时要检查牌组是否为空,避免抛出数组越界异常。
内容的提问来源于stack exchange,提问作者Cody Baird
相关产品推荐
相关产品推荐

