Java游戏开发问题:循环外变量二次调用时异常变更
队伍分数调整功能异常问题排查与修复
问题现象
- 第二次调用分数调整功能时,之前修改过的队伍分数会被意外覆盖。例如先修改红队分数,再修改蓝队分数,红队分数会被改成蓝队的分数值。
- 类成员变量
Choice无法直接在内部匿名类中使用,只能赋值给tempColor,且编译器提示该变量需为final,不确定是否与上述异常相关。
操作流程
菜单触发mniAdjustScoreActionPerformed方法,调用adjustScore();选择对应队伍后,执行changeScore()方法完成分数修改。
原始代码
触发方法
private void mniAdjustScoreActionPerformed(java.awt.event.ActionEvent evt) { adjustScore(); }
选择队伍方法
private void adjustScore() { //FIXME 第二次调用时会同时修改第一次和第二次选中队伍的分数,疑似意外循环? // 设置组件可见性 df.CT.lblChoose.setText("Which team needs adjustment?"); setCTButtons(); df.CT.setVisible(true); df.CT.btnAdjust.setVisible(false); df.CT.jspAdjust.setVisible(false); df.CT.txtName.setVisible(false); // 点击按钮设置Choice并调用changeScore df.CT.btnRedTeam.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { Choice = "red"; changeScore(); Choice = ""; } }); df.CT.btnBlueTeam.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { Choice = "blue"; changeScore(); Choice = ""; } }); df.CT.btnCyanTeam.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { Choice = "cyan"; changeScore(); Choice = ""; } }); df.CT.btnYellowTeam.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { Choice = "yellow"; changeScore(); Choice = ""; } }); df.CT.btnGreenTeam.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { Choice = "green"; changeScore(); Choice = ""; } }); df.CT.btnPurpleTeam.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { Choice = "purple"; changeScore(); Choice = ""; } }); }
分数修改方法
private void changeScore(){ //TODO 修复:第二次调用调整分数时,会同时修改第二支和第一支队伍的分数,疑似意外循环? // 设置第二阶段操作的组件可见性 df.CT.lblChoose.setVisible(true); df.CT.jspAdjust.setVisible(true); df.CT.btnAdjust.setVisible(true); df.CT.txtName.setVisible(false); // 将Choice赋值给tempColor(后续需确认为何必须这么做) String tempColor = Choice; // 调整按钮的点击事件 df.CT.btnAdjust.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { // 获取 spinner 中的分数值 int tempScore = (Integer) df.CT.jspAdjust.getValue(); // 遍历队伍列表,找到TeamColor匹配tempColor的队伍 for(int i=0; i<df.orderedTeamList.size(); i++){ String tempColor2 = df.orderedTeamList.getElementAt(i).getTeamColor(); // 调试用弹窗:显示各变量值 JOptionPane.showMessageDialog(null, "Choice: "+ Choice + "\n tempColor: " + tempColor + "\n tempScore: " + tempScore + "\n tempColor2: " + tempColor2, "NOTE", JOptionPane.ERROR_MESSAGE); // 匹配颜色后修改对应队伍的分数 if (tempColor2.equals(tempColor)){ if (tempColor.equals("red")){ df.txtScoreT1.setBackground(Color.RED); df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); } else if (tempColor.equals("blue")){ df.txtScoreT2.setBackground(Color.RED); df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); } else if (tempColor.equals("cyan")){ df.txtScoreT3.setBackground(Color.RED); df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); } else if (tempColor.equals("yellow")){ df.txtScoreT4.setBackground(Color.RED); df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); } else if (tempColor.equals("green")){ df.txtScoreT5.setBackground(Color.RED); df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); } else if (tempColor.equals("purple")){ df.txtScoreT6.setBackground(Color.RED); df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); } else{ JOptionPane.showMessageDialog(null, "Team is not active!", "Team Error", JOptionPane.ERROR_MESSAGE); } } } // 更新总分并关闭窗口 updatePoints(); df.CT.setVisible(false); } }); }
问题根源
- 重复添加ActionListener:每次调用
adjustScore()都会给队伍按钮和btnAdjust添加新的监听器。比如第一次调红队时,给btnAdjust加了红队的修改逻辑;第二次调蓝队时,又加了蓝队的修改逻辑。点击btnAdjust时,所有已添加的监听器都会执行,导致多支队伍的分数被修改。 - 成员变量
Choice的生命周期冲突:匿名内部类引用外部变量时要求变量为final或等效final,所以用tempColor是合理的,但重复添加的旧监听器会持有之前的tempColor值,执行时会修改对应旧队伍的分数。
修复方案
1. 重构changeScore()为带参数方法
直接把目标队伍颜色作为参数传入,避免依赖成员变量Choice,同时解决匿名内部类的变量限制问题。
2. 避免重复添加监听器
每次给btnAdjust添加新监听器前,先移除所有旧监听器,防止多次触发。
修改后的代码
调整后的adjustScore()方法
private void adjustScore() { // 原有UI设置代码不变 df.CT.lblChoose.setText("Which team needs adjustment?"); setCTButtons(); df.CT.setVisible(true); df.CT.btnAdjust.setVisible(false); df.CT.jspAdjust.setVisible(false); df.CT.txtName.setVisible(false); // 直接传递颜色参数给changeScore,简化逻辑 df.CT.btnRedTeam.addActionListener(evt -> changeScore("red")); df.CT.btnBlueTeam.addActionListener(evt -> changeScore("blue")); df.CT.btnCyanTeam.addActionListener(evt -> changeScore("cyan")); df.CT.btnYellowTeam.addActionListener(evt -> changeScore("yellow")); df.CT.btnGreenTeam.addActionListener(evt -> changeScore("green")); df.CT.btnPurpleTeam.addActionListener(evt -> changeScore("purple")); }
调整后的changeScore()方法
private void changeScore(String targetColor) { // 原有UI设置代码不变 df.CT.lblChoose.setVisible(true); df.CT.jspAdjust.setVisible(true); df.CT.btnAdjust.setVisible(true); df.CT.txtName.setVisible(false); // 先移除btnAdjust的所有旧监听器,避免重复执行 for (ActionListener listener : df.CT.btnAdjust.getActionListeners()) { df.CT.btnAdjust.removeActionListener(listener); } df.CT.btnAdjust.addActionListener(evt -> { int tempScore = (Integer) df.CT.jspAdjust.getValue(); for(int i=0; i<df.orderedTeamList.size(); i++){ String teamColor = df.orderedTeamList.getElementAt(i).getTeamColor(); if (teamColor.equals(targetColor)){ // 用switch简化分支逻辑 switch(targetColor) { case "red": df.txtScoreT1.setBackground(Color.RED); break; case "blue": df.txtScoreT2.setBackground(Color.RED); break; case "cyan": df.txtScoreT3.setBackground(Color.RED); break; case "yellow": df.txtScoreT4.setBackground(Color.RED); break; case "green": df.txtScoreT5.setBackground(Color.RED); break; case "purple": df.txtScoreT6.setBackground(Color.RED); break; default: JOptionPane.showMessageDialog(null, "Team is not active!", "Team Error", JOptionPane.ERROR_MESSAGE); return; } df.orderedTeamList.getElementAt(i).setTeamScore(tempScore); break; // 找到目标队伍后直接退出循环,提升效率 } } updatePoints(); df.CT.setVisible(false); }); }
额外优化建议(可选)
- 初始化时给队伍按钮添加一次监听器即可,不用每次调用
adjustScore()都重复添加,减少冗余操作。 - 用
Map关联队伍颜色、按钮和对应的分数文本框,进一步简化分支逻辑,避免大量switch或if-else。
内容的提问来源于stack exchange,提问作者Nicholas Grencz
相关产品推荐
相关产品推荐

