Java方法变量传递问题:牌组抽牌程序重复抽牌故障排查
问题与解决方案
问题描述
我实现了一个从牌组随机抽取卡牌并防止重复抽取的类方法,但该方法失效,原因是迭代值无法在方法调用间持续传递。请问是否可以避免方法中的变量被销毁?以下是相关代码:
Deck类代码
public class Deck { // Array for the deck of cards private String[] DeckOfCards = { " 2 of spades ", " 3 of spades ", " 4 of spades ", " 5 of spades ", " 6 of spades ", " 7 of spades ", " 8 of spades ", " 9 of spades ", " 10 of spades ", " Jack of spades ", " Queen of spades ", " King of Spades ", " Ace of spades ", " 2 of clubs ", " 3 of clubs ", " 4 of clubs ", " 5 of clubs ", " 6 of clubs ", " 7 of clubs ", " 8 of clubs ", " 9 of clubs ", " 10 of clubs ", " Jack of clubs ", " Queen of clubs ", " King of Clubs ", " Ace of clubs ", " 2 of diamonds ", " 3 of diamonds ", " 4 of diamonds ", " 5 of diamonds ", " 6 of diamonds ", " 7 of diamonds ", " 8 of diamonds ", " 9 of diamonds ", " 10 of diamonds ", " Jack of diamonds ", " Queen of diamonds ", " King of Diamonds ", " Ace of diamonds ", " 2 of hearts ", " 3 of hearts ", " 4 of hearts ", " 5 of hearts ", " 6 of hearts ", " 7 of hearts ", " 8 of hearts ", " 9 of hearts ", " 10 of hearts ", " Jack of hearts ", " Queen of hearts ", " King of Hearts ", " Ace of hearts " }; // Declaring other variables int[] alreadyDrawnCards = new int[53]; int drawnCard; int i = 0; int j = 0; /** * method used to draw cards */ protected int draw() { drawnCard = (int)(Math.random() * 53); while(j < i) { if(drawnCard == alreadyDrawnCards[j]) { drawnCard = (int)(Math.random() * 53); j = -1; } j++; } if(i < 53) { alreadyDrawnCards[i] = drawnCard; // add drawn card to drawn cards i++; } System.out.println("The" + DeckOfCards[drawnCard] + "was drawn from the deck"); return drawnCard; } }
调用代码
public class BlackjackGameSimulator { public static void main(String[] args) { Deck BlackJackDeck = new Deck(); BlackJackDeck.draw(); } }
问题分析与解决
核心问题
你误解了变量销毁的问题:类成员变量i、alreadyDrawnCards、j都会随着Deck实例的存在而保留,不会被销毁。真正导致方法失效的是**j作为类成员变量,每次调用draw()后没有重置为0**,导致后续抽牌时循环逻辑混乱。
比如第一次调用draw()后,j的值会走到i的位置(比如i=1时j=1),第二次调用时j从1开始,跳过了对alreadyDrawnCards[0]的检查,就可能抽到重复卡牌。
修复步骤
将临时循环变量
j移到方法内部
把j的声明放到draw()方法里,每次调用时重新初始化为0,这样每次抽牌的循环都会从头检查已抽卡牌:protected int draw() { int j = 0; // 移到方法内,每次调用重置 drawnCard = (int)(Math.random() * 53); while(j < i) { if(drawnCard == alreadyDrawnCards[j]) { drawnCard = (int)(Math.random() * 53); j = -1; } j++; } if(i < 53) { alreadyDrawnCards[i] = drawnCard; i++; } System.out.println("The" + DeckOfCards[drawnCard] + "was drawn from the deck"); return drawnCard; }优化抽牌逻辑(更高效的方案)
当前的重复检测方式效率很低,每次抽牌都要遍历已抽数组。更合理的方式是初始化时洗牌,然后按顺序发牌,完全避免重复检测:import java.util.Random; public class Deck { private String[] deckOfCards = { " 2 of spades ", " 3 of spades ", " 4 of spades ", " 5 of spades ", " 6 of spades ", " 7 of spades ", " 8 of spades ", " 9 of spades ", " 10 of spades ", " Jack of spades ", " Queen of spades ", " King of Spades ", " Ace of spades ", " 2 of clubs ", " 3 of clubs ", " 4 of clubs ", " 5 of clubs ", " 6 of clubs ", " 7 of clubs ", " 8 of clubs ", " 9 of clubs ", " 10 of clubs ", " Jack of clubs ", " Queen of clubs ", " King of Clubs ", " Ace of clubs ", " 2 of diamonds ", " 3 of diamonds ", " 4 of diamonds ", " 5 of diamonds ", " 6 of diamonds ", " 7 of diamonds ", " 8 of diamonds ", " 9 of diamonds ", " 10 of diamonds ", " Jack of diamonds ", " Queen of diamonds ", " King of Diamonds ", " Ace of diamonds ", " 2 of hearts ", " 3 of hearts ", " 4 of hearts ", " 5 of hearts ", " 6 of hearts ", " 7 of hearts ", " 8 of hearts ", " 9 of hearts ", " 10 of hearts ", " Jack of hearts ", " Queen of hearts ", " King of Hearts ", " Ace of hearts " }; private int currentCardIndex = 0; // 构造方法里洗牌 public Deck() { Random random = new Random(); // Fisher-Yates洗牌算法 for (int k = deckOfCards.length - 1; k > 0; k--) { int swapIndex = random.nextInt(k + 1); String temp = deckOfCards[k]; deckOfCards[k] = deckOfCards[swapIndex]; deckOfCards[swapIndex] = temp; } } protected String draw() { if (currentCardIndex >= deckOfCards.length) { System.out.println("牌组已经抽完了"); return null; } String drawnCard = deckOfCards[currentCardIndex++]; System.out.println("抽到了:" + drawnCard); return drawnCard; } }这个方案不仅避免了重复问题,代码更简洁,效率也更高。
总结
- 类成员变量不会随方法调用销毁,你的问题是错误地将临时循环变量设为成员变量导致状态混乱。
- 把临时变量移到方法内部初始化即可解决原有逻辑问题;推荐使用洗牌法实现抽牌,更符合真实场景且高效。
内容的提问来源于stack exchange,提问作者Puppy_With_Pinecone
相关产品推荐
相关产品推荐

