Java:在交换机案例中无法解决问题 - 为什么?

时间:2017-05-31 09:40:50

标签: java switch-statement

我编写了一个游戏。 我使用switch case来实现状态机。 问题是,在ZIEHEN_SP的情况下,break语句不起作用。 当我调试它时,编译器只是跳过break语句并转到下一个案例ZIEHEN_BA。

我评论了编译器忽略break语句的部分。

为什么?

import java.util.*;
import java.util.Scanner; 
import java.io.*;

class BlackJack2 {

    static int chips = 100;
    static int einsatz = 0;

    enum State { INIT, EINSATZ,ZIEHEN_SP, ZIEHEN_BA}
    static State state = State.INIT;

    static ArrayList<Integer> bankKarten = new ArrayList<Integer>();
    static ArrayList<Integer> spielerKarten = new ArrayList<Integer>();

    static Scanner scanner = new Scanner(System.in); 
    static String s = "";
    static int eingabe = 0;

    static void init(){
        System.out.println("\nEin neues Spiel beginnt: ");
        bankKarten.clear();
        spielerKarten.clear();
        bankKarten.add(giveCard());
        spielerKarten.add(giveCard());
    }

    static void chipsSetzen(){
        einsatz = 0;
        if(chips == 0){
            System.out.print("\nSie haben " + chips + " Chips!");
            System.exit(1);
        }

        do{
            System.out.print("\nSie haben " + chips + " Chips");
            System.out.print("\nWie viel moechten Sie setzen? ");
            try{
                einsatz = Integer.parseInt(scanner.next());
            } catch(Exception e){

            }
        } while(einsatz <= 0 || einsatz > chips);

        chips -= einsatz;
    }

    static int sumSpielerKarten(){
        int sum=0;
        for(int i=0; i<spielerKarten.size(); i++){
            sum +=spielerKarten.get(i);
        }
        return sum;
    }

    static int sumBankKarten(){
        int sum=0;
        for(int i=0; i<bankKarten.size(); i++){
            sum +=bankKarten.get(i);
        }
        return sum;
    }

    static int giveCard(){
        return (int)(Math.random()*11+1);
    }

    static boolean oneMoreCard(){
        int ss = sumSpielerKarten();

        if(ss >= 21){
            return false;
        } else {
            do{
                System.out.print("\nMoechten sie eine witere Karte ziehen? (y/n): ");
                s = scanner.next();
                if(s.equals("y")){
                    return true;
                } else if(s.equals("n")){
                    return false;
                }
            } while(!s.equals("y") || !s.equals("n"));
        }
        return false;
    }

    static String evaluateWinner(int s, int b){
        String ret = "";
        if(b > 21 || (s > b && s<=21) || s == 21 && b != 21){ 
            ret = "Player";
        } else if(s > 21 || b > s || b == 21 && s != 21){
            ret = "Bank";
        } else if(b == s){
            ret = "Both";
        }
        return ret;
    }

    static int updateMoney(int s, int b){
        String winner = evaluateWinner(s, b);
        int newChips = 0;

        if(winner == "Player"){
            newChips =  einsatz*2 + chips;
        } else if(winner == "Both"){
            newChips =  einsatz + chips;
        } else if(winner == "Bank"){
            newChips = chips;
        }

        System.out.println("Winner: "+ winner);
        return newChips;
    }

    static void showCards(){
        System.out.print("\nBank:\t");
        for(int i=0; i<bankKarten.size(); i++){
            System.out.print( "[" + bankKarten.get(i) + "]");
        }
        System.out.println("\t= " + sumBankKarten());

        System.out.print("Player:\t");
        for(int i=0; i<spielerKarten.size(); i++){
            System.out.print( "[" + spielerKarten.get(i) + "]");
        }
        System.out.println("\t= " + sumSpielerKarten());
    }

    static void banksTurn(){
        int sb = sumBankKarten();
        int ss = sumSpielerKarten();
        if(sb != 21 && ss != 21 && ss < 21){
            while(sb < 17 ||  (ss > sb && sb < 17)){
                bankKarten.add(giveCard());
            }
        }
        updateMoney(ss, sb);
    }

    public static void main(String args[]){
        while(true){
            switch(state){
                case INIT:
                    init();
                    state = State.EINSATZ;
                    break;
                case EINSATZ:
                    chipsSetzen();
                    state = State.ZIEHEN_SP;
                    break;
                case ZIEHEN_SP:
                    showCards();
                    while(oneMoreCard()){
                        spielerKarten.add(giveCard());
                        showCards();
                    }
                    state = State.ZIEHEN_BA;
                    break; // << Compiler ignores this statement and goes directly to case ZIEHEN_BA
                case ZIEHEN_BA:
                    banksTurn();
                    state = State.INIT;
                    break;
            }
        }
    }

}

5 个答案:

答案 0 :(得分:3)

因为您将state更改为与State.ZIEHEN_BA条件匹配的值:

 state = State.ZIEHEN_BA;

所以这里:

while(true){
   ... 
       state = State.ZIEHEN_BA;
       break; 

    case ZIEHEN_BA:
       banksTurn();
       state = State.INIT;
       break;
   ...
}

case ZIEHEN_BA在循环的下一次迭代中执行。

Eclipse显示的可能是在运行时或编译器执行的JVM的优化。您可以反汇编课程以获取更多信息。

编辑

我已完成测试,我认为这不是编译器优化。

看看这个最小的例子,我没有在这种情况下设置状态:

public class TestSwitch {

    public enum MyEnum {
        A, B
    };

    public static void main(String[] args) {

        MyEnum state = MyEnum.A;

        while (true) {
            switch (state) {

            case A:             
                break;

            case B:
                break;
            }
        }
    }
}

这是main()方法的反汇编代码:

 public static void main(java.lang.String[]);
   Code:
      0: getstatic     #18                 // Field a/TestSwitch$MyEnum.A:La/TestSwitch$MyEnum;
      3: astore_1
      4: invokestatic  #24                 // Method $SWITCH_TABLE$a$TestSwitch$MyEnum:()[I
      7: aload_1
      8: invokevirtual #27                 // Method a/TestSwitch$MyEnum.ordinal:()I
     11: iaload
     12: tableswitch   { // 1 to 2
                    1: 36
                    2: 39
              default: 39
         }
     36: goto          4
     39: goto          4

然后查看我在案例B中设置状态的版本以便输入案例B:

public class TestSwitch {

    public enum MyEnum {
        A, B
    };

    public static void main(String[] args) {

        MyEnum state = MyEnum.A;

        while (true) {
            switch (state) {

            case A:
                state = MyEnum.B;
                break;

            case B:
                break;
            }
        }
    }
}

这是main()方法的反汇编代码:

 public static void main(java.lang.String[]);
   Code:
      0: getstatic     #18                 // Field a/TestSwitch$MyEnum.A:La/TestSwitch$MyEnum;
      3: astore_1
      4: invokestatic  #24                 // Method $SWITCH_TABLE$a$TestSwitch$MyEnum:()[I
      7: aload_1
      8: invokevirtual #27                 // Method a/TestSwitch$MyEnum.ordinal:()I
     11: iaload
     12: tableswitch   { // 1 to 2
                    1: 36
                    2: 43
              default: 43
         }
     36: getstatic     #31                 // Field a/TestSwitch$MyEnum.B:La/TestSwitch$MyEnum;
     39: astore_1
     40: goto          4
     43: goto          4

此编译代码中没有优化。

案例A执行后:

     36: getstatic     #31                 // Field a/TestSwitch$MyEnum.B:La/TestSwitch$MyEnum;
     39: astore_1

下一条指令是转到循环:

40: goto          4

因此,优化可能是由JVM或Eclipse调试器在运行时执行的。

答案 1 :(得分:1)

编译器优化了您的代码: - )

当你将开关变量设置为State.ZIEHEN_BA时,在(while (true)和重新进入开关之间)没有任何东西可以执行,而这正是下一行要执行的。

我不确定它是否应该以这种方式运行(在开关内部更改变量会导致检查以下情况)但在您的情况下我完全同意编译器。

正如您在此示例中所看到的,此行为并非总是如此:

public static void main(String[] args) {
    int i = 3;
    switch(i) {
    case 1:
        System.out.println("1:");
        break;
    case 2:
        System.out.println("2:");
        break;
    case 3:
        System.out.println("3:");
        i = 5;
        break;
    case 4:
        System.out.println("4:");
        break;
    case 5:
        System.out.println("5:");
        i = 5;
        break;
    case 6:
        System.out.println("6:");
        break;
    case 7:
        System.out.println("7:");
        break;
        default:
            break;
    }
    System.out.println("I = " + i);
}

结果

 3:
 I = 5

答案 2 :(得分:0)

这是一项优化。当您将状态设置为ZIEHEN_BA时,编译器知道它将作为下一步到达那里。如果没有优化,就会有一些附加的步骤,但无论如何它都会实现:

设置状态;做休息;转到while(true),现在进行切换......它到达ZIEHEN_BA。所以这相当于直接去那里。

答案 3 :(得分:0)

实际上,编译器并没有忽略中断。

在现有案例陈述中设置ZIEHEN_BA时 因此,在调用break之后,它会在while(ture)循环的下一次迭代中直接进入ZIEHEN_BA .... :)

它似乎可以通过忽略break直接转到declare @table table (starttime datetime) insert into @table values ('2017-05-31 13:00:00'), ('2017-05-31 13:15:00'), ('2017-05-31 13:30:00'), ('2017-05-31 13:45:00'), ('2017-05-31 14:00:00'), ('2017-05-31 14:15:00'), ('2017-05-31 14:30:00'), ('2017-05-31 14:45:00'), ('2017-05-31 15:00:00'), ('2017-05-31 15:15:00') Select dateadd(hour,datepart(hour,starttime), cast(cast(starttime as date)as datetime)) aggrHour from @table group by dateadd(hour,datepart(hour,starttime), cast(cast(starttime as date)as datetime)) having count(starttime) = 4 ,但它会在接下来的迭代中进入//Custom Controller app.controller('custom_controller', function($http, $scope, $compile) { $scope.change = function() { switch ($scope.data['model']) { case 'Telecom': $scope.controller = 'Telecom_Controller'; break; case 'Netflix': $scope.controller = 'Netflix_Controller'; break; case 'Bank': $scope.controller = 'Bank_Controller'; break; case 'Beauty': $scope.controller = 'Beauty_Controller'; break; case 'Mutual Funds': $scope.controller = 'MF_Controller'; break; case 'Motor Insurance': $scope.controller = 'MI_Controller'; break; case 'Job Hunt': $scope.controller = 'Job_Controller'; break; default: alert("None Selected"); break; } }; });

答案 4 :(得分:0)

你写的主程序是一个牢不可破的循环。如果我们正确地看到代码,那么只要遇到某个案例,就会将状态分配给另一个案例。而且你永远不会结束循环。 while循环不知道在哪里停止。不完全确定你想做什么。

仅供参考,不会跳过中断,但只会破坏交换机循环。我猜你期望打破while循环......

如果您希望代码在特定点停止,请为while循环设置一个中断。再次将break;放在开关内部不起作用,因为它会破坏开关循环。相反,尝试在while之前设置Boolean variable,并将变量值更改为false,而不想让循环中断。