You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        }
    });
}

问题根源

  1. 重复添加ActionListener:每次调用adjustScore()都会给队伍按钮和btnAdjust添加新的监听器。比如第一次调红队时,给btnAdjust加了红队的修改逻辑;第二次调蓝队时,又加了蓝队的修改逻辑。点击btnAdjust时,所有已添加的监听器都会执行,导致多支队伍的分数被修改。
  2. 成员变量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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 19:00:53