Java: Cant break after a case in a switch

I programmed a game. I used switch case to implement a state machine. The problem is that in case ZIEHEN_SP the break statement doesn't work. When I debug it, the compiler just step over the break statement and goes to the next case ZIEHEN_BA.

I commented the part where the compiler ignores the break statement.

Why?

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

}

Because you change state to a value that matches with State.ZIEHEN_BA condition :

 state = State.ZIEHEN_BA;

So here :

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

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

the case ZIEHEN_BA is executed at the next iteration of the loop.

What Eclipse shows may be an optimization of the JVM performed at runtime or by the compiler. You could disassemble the class to have more information.


EDIT

I have done the test and i don't think it is a compiler optimization.

Look at this minimal example where I don't set the state in the case :

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

Here is the disassembled code of the main() method :

 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

And look at the version where I set the state in case A to enter in case 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;
            }
        }
    }
}

Here is the disassembled code of the main() method :

 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

There is not optimization in this compiled code.

After case A execution :

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

the next instruction is a goto to the loop :

40: goto          4

So the optimization is probably performed at runtime by the JVM or the Eclipse debugger.


The compiler optimized your code :-)

As you set the switch variable to State.ZIEHEN_BA and there's nothing to be executed in between ( while (true) and reentering the switch) that's exactly the next lines to be executed.

I'm not sure if it's supposed to behave that way (changing variable switched on inside switch will cause following cases to be checked) but in your case I totally agree with the compiler.

As you can see in this example, this behaviour is not always the case:

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

Results in

 3:
 I = 5

This is an optimization. When you set the state to ZIEHEN_BA, the compiler knows it will get there as a next step. Without optimization, there would just be some aditional steps, but it will get there anyway:

Set the state; do the break; goto the while(true), now do the switch and...it gets to ZIEHEN_BA. So that is equivalent to going there directly.

链接地址: http://www.djcxy.com/p/84440.html

上一篇: 在switch语句中初始化时未定义的变量?

下一篇: Java:不能在交换机中处理一个案例