96

为什么编译器不会在 switch 中的每个代码块之后自动放置 break 语句?是因为历史原因吗?您希望何时执行多个代码块?

4

17 回答 17

96

有时将多个案例与同一个代码块相关联会很有帮助,例如

case 'A':
case 'B':
case 'C':
    doSomething();
    break;

case 'D':
case 'E':
    doSomethingElse();
    break;

等等。只是一个例子。

根据我的经验,“失败”并为一个案例执行多个代码块通常是不好的风格,但在某些情况下可能会有用处。

于 2010-04-25T23:02:50.257 回答
33

Java 来自 C,这就是来自 C 的语法。

有时您希望多个 case 语句只有一个执行路径。下面是一个示例,它将告诉您一个月中有多少天。

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                numDays = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                numDays = 30;
                break;
            case 2:
                if ( ((year % 4 == 0) && !(year % 100 == 0))
                     || (year % 400 == 0) )
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = " + numDays);
    }
}
于 2010-04-25T23:05:25.960 回答
33

从历史上看,这是因为case本质上定义了一个,label也称为调用的目标点goto。switch 语句及其相关案例实际上只是代表了一个多路分支,它具有多个进入代码流的潜在入口点。

话虽如此,几乎无数次都注意到这break几乎总是您希望在每个案例结束时拥有的默认行为。

于 2010-04-25T23:20:23.213 回答
15

我认为这是一个错误。作为一种语言结构,它与默认值一样容易break,而是有一个fallthrough关键字。我编写和阅读的大多数代码在每个案例之后都会中断。

于 2010-04-25T23:07:48.207 回答
13

您可以通过案例失败来做各种有趣的事情。

例如,假设您想针对所有情况执行特定操作,但在某些情况下您想要执行该操作以及其他操作。使用带有 fall-through 的 switch 语句会很容易。

switch (someValue)
{
    case extendedActionValue:
        // do extended action here, falls through to normal action
    case normalActionValue:
    case otherNormalActionValue:
        // do normal action here
        break;
}

当然,很容易忘记breakcase 结尾的语句并导致意外行为。当你省略 break 语句时,好的编译器会警告你。

于 2010-04-25T23:03:42.427 回答
7

为什么编译器不会在 switch 中的每个代码块之后自动放置 break 语句?

撇开能够在几种情况下使用相同块的良好愿望(可能是特殊情况)......

是因为历史原因吗?您希望何时执行多个代码块?

goto它主要是为了与 C 兼容,并且可以说是古代关键字在地球上漫游时的古老黑客。当然,它确实实现了一些令人惊奇的事情,例如Duff's Device,但无论这是赞成还是反对它的观点……充其量是争论的。

于 2010-04-25T23:12:06.353 回答
6

breakafter switchcase用于避免 switch 语句中的失败。虽然有趣的是,现在这可以通过JEP-325实现的新形成的开关标签来实现。

通过这些更改,可以避免break使用每个开关,如下所示:-case

public class SwitchExpressionsNoFallThrough {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int value = scanner.nextInt();
        /*
         * Before JEP-325
         */
        switch (value) {
            case 1:
                System.out.println("one");
            case 2:
                System.out.println("two");
            default:
                System.out.println("many");
        }

        /*
         * After JEP-325
         */
        switch (value) {
            case 1 ->System.out.println("one");
            case 2 ->System.out.println("two");
            default ->System.out.println("many");
        }
    }
}

使用 JDK-12 执行上述代码时比较输出可以看作是

//input
1
// output from the implementation before JEP-325
one
two
many
// output from the implementation after JEP-325
one

//input
2
// output from the implementation before JEP-325
two
many
// output from the implementation after JEP-325
two

当然,事情没有改变

// input
3
many // default case match
many // branches to 'default' as well
于 2018-09-08T19:14:04.447 回答
4

因此,如果您需要多个案例来做同样的事情,您不必重复代码:

case THIS:
case THAT:
{
    code;
    break;
}

或者您可以执行以下操作:

case THIS:
{
   do this;
}
case THAT:
{
   do that;
}

以级联方式。

如果你问我,真的很容易出错/混淆。

于 2010-04-25T23:02:56.647 回答
4

就历史记录而言,Tony Hoare 在 1960 年代的“结构化编程”革命期间发明了案例陈述。Tony 的 case 语句支持每个 case 有多个标签,并支持自动退出,没有臭名昭著的break语句。显式的要求break来自 BCPL/B/C 行。Dennis Ritchie 写道(在 ACM HOPL-II 中):

例如,当我们在 1960 年代学习 BCPL switchon 语句时,语言中不存在从 B 和 C 语句中转义的 endcase,因此重载 break 关键字以逃避 B 和 C 的 switch 语句是由于发散进化而不是有意识的改变。

我还没有找到任何关于 BCPL 的历史著作,但 Ritchie 的评论表明这break或多或少是一个历史事故。BCPL 后来解决了这个问题,但也许 Ritchie 和 Thompson 都忙于发明 Unix,而无暇顾及这样的细节 :-)

于 2010-04-26T01:44:43.087 回答
3

Java 源自 C,其遗产包括一种称为Duff's Device的技术。break;这是一种优化,它依赖于在没有声明的情况下控制从一个案例到下一个案例的事实。当 C 被标准化时,已经有很多类似“在野外”的代码,而改变语言来破坏这种结构会适得其反。

于 2010-04-25T23:10:09.280 回答
3

正如人们之前所说,这是允许跌倒,这不是一个错误,这是一个特性。如果过多的语句让您烦恼,您可以通过使用语句break轻松摆脱它们。return这实际上是一个很好的做法,因为你的方法应该尽可能小(为了可读性和可维护性),所以一个switch语句对于一个方法来说已经足够大了,因此,一个好的方法不应该包含其他任何东西,这是一个例子:

public class SwitchTester{
    private static final Log log = LogFactory.getLog(SwitchTester.class);
    public static void main(String[] args){
        log.info(monthsOfTheSeason(Season.WINTER));
        log.info(monthsOfTheSeason(Season.SPRING));
        log.info(monthsOfTheSeason(Season.SUMMER));
        log.info(monthsOfTheSeason(Season.AUTUMN));
    }

    enum Season{WINTER, SPRING, SUMMER, AUTUMN};

    static String monthsOfTheSeason(Season season){
        switch(season){
            case WINTER:
                return "Dec, Jan, Feb";
            case SPRING:
                return "Mar, Apr, May";
            case SUMMER:
                return "Jun, Jul, Aug";
            case AUTUMN:
                return "Sep, Oct, Nov";
            default:
                //actually a NullPointerException will be thrown before reaching this
                throw new IllegalArgumentException("Season must not be null");
        }        
    }
}   

执行打印:

12:37:25.760 [main] INFO lang.SwitchTester - Dec, Jan, Feb
12:37:25.762 [main] INFO lang.SwitchTester - Mar, Apr, May
12:37:25.762 [main] INFO lang.SwitchTester - Jun, Jul, Aug
12:37:25.762 [main] INFO lang.SwitchTester - Sep, Oct, Nov

正如预期的那样。

于 2017-07-19T10:39:40.660 回答
0

如果没有编译器添加的自动中断,则可以使用 switch/case 来测试条件,例如1 <= a <= 3从 1 和 2 中删除 break 语句。

switch(a) {
  case 1: //I'm between 1 and 3
  case 2: //I'm between 1 and 3
  case 3: //I'm between 1 and 3
          break;
}
于 2010-04-25T23:02:35.657 回答
0

因为在某些情况下,您希望流过第一个块,例如避免在多个块中编写相同的代码,但仍然能够将它们划分为 mroe 控制。还有很多其他原因。

于 2010-04-25T23:04:00.210 回答
0

这是一个老问题,但实际上我今天遇到了使用 case without break 语句。当你需要按顺序组合不同的功能时,不使用break实际上非常有用。

例如,使用 http 响应代码使用时间令牌对用户进行身份验证

服务器响应代码 401 - 令牌已过时 -> 重新生成令牌并登录用户。
服务器响应代码 200 - 令牌正常 -> 登录用户。

案例陈述:

case 404:
case 500:
        {
            Log.v("Server responses","Unable to respond due to server error");
            break;
        }
        case 401:
        {
             //regenerate token
        }
        case 200:
        {
            // log in user
            break;
        }

使用它,您不需要为 401 响应调用登录用户函数,因为当重新生成令牌时,运行时会跳转到案例 200。

于 2014-09-26T14:55:02.503 回答
0

您可以轻松地将其他类型的数字、月份、计数分开。
如果在这种情况下,这会更好;

public static void spanishNumbers(String span){

    span = span.toLowerCase().replace(" ", "");
    switch (span){
     case "1":    
     case "jan":  System.out.println("uno"); break;    
     case "2":      
     case "feb":  System.out.println("dos"); break;    
     case "3":     
     case "mar":  System.out.println("tres"); break;   
     case "4":   
     case "apr":  System.out.println("cuatro"); break;
     case "5":    
     case "may":  System.out.println("cinco"); break;
     case "6":     
     case "jun":  System.out.println("seis"); break;
     case "7":    
     case "jul":  System.out.println("seite"); break;
     case "8":    
     case "aug":  System.out.println("ocho"); break;
     case "9":   
     case "sep":  System.out.println("nueve"); break;
     case "10":    
     case "oct": System.out.println("diez"); break;
     }
 }
于 2016-06-08T02:59:37.147 回答
0

我现在正在处理我在breakswitch 语句中需要的项目,否则代码将无法工作。请耐心等待,我会给你一个很好的例子来说明为什么你需要break在你的 switch 语句中。

假设您有三种状态,一种等待用户输入数字,第二种等待计算,第三种打印总和。

在这种情况下,您有:

  1. State1 - 等待用户输入数字
  2. State2 - 打印总和
  3. state3 - 计算总和

查看状态,您可能希望从state1开始,然后是 state3,最后是 state2。否则我们将只打印用户输入而不计算总和。再次澄清一下,我们等待用户输入一个值,然后计算总和并打印总和。

这是一个示例代码:

while(1){
    switch(state){
      case state1:
        // Wait for user input code
        state = state3; // Jump to state3
        break;
      case state2:
        //Print the sum code
        state = state3; // Jump to state3;
      case state3:
        // Calculate the sum code
        state = wait; // Jump to state1
        break;
    }
}

如果我们不使用break,它将按照state1state2state3的顺序执行。但是使用break,我们可以避免这种情况,并且可以在正确的过程中进行排序,即从 state1 开始,然后 state3 和最后但并非最不重要的 state2。

于 2017-05-03T22:27:52.967 回答
-1

没错,因为通过一些巧妙的放置,您可以级联执行块。

于 2010-04-25T23:03:03.520 回答