为什么我们需要突破案例陈述?

时间:2010-04-25 22:58:15

标签: java switch-statement case language-design break

为什么编译器在交换机中的每个代码块之后都不会自动放置break语句?这是出于历史原因吗?您何时需要执行多个代码块?

17 个答案:

答案 0 :(得分:90)

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

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

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

等。只是一个例子。

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

答案 1 :(得分:29)

Historically,这是因为case基本上定义了label,也称为goto来电的target point。 switch语句及其相关案例实际上只代表一个多路分支,在代码流中有多个潜在的入口点。

所有这一切,已经注意到几乎无限次break几乎总是你在每个案例结束时都会有的默认行为。

答案 2 :(得分:27)

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

答案 3 :(得分:13)

你可以通过案例落实来做各种有趣的事情。

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

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

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

答案 4 :(得分:13)

我认为这是一个错误。作为一种语言结构,将break作为默认值并使用fallthrough关键字变得同样容易。我编写和阅读的大多数代码都会在每个案例后都有一段时间。

答案 5 :(得分:6)

  

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

不考虑良好的希望能够在几种情况下使用相同的块(可能是特殊的)...

  

是出于历史原因吗?您何时需要执行多个代码块?

它主要是为了与C兼容,可以说是goto关键词在地球上漫游时的古代黑客攻击。 确实启用了一些令人惊奇的事情,当然,例如Duff's Device,但这是否有利于它还是反对的......充其量只是争论。

答案 6 :(得分:4)

因此,如果您需要多个案例来执行相同的操作,则不必重复代码:

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

或者你可以做以下事情:

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

以级联方式。

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

答案 7 :(得分:3)

在切换break之后使用case来避免switch语句中的失败。尽管有趣的是,现在可以通过通过JEP-325实现的新形成的开关标签来实现。

通过这些更改,可以避免每次切换break的{​​{1}},如进一步说明:-

case

executing the above code with JDK-12上,比较输出可以看作是

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

//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

答案 8 :(得分:3)

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

  

例如,语言中不存在从BCPL switchon语句中转义的endcase   当我们在20世纪60年代学会它时,所以break关键字的重载要逃避   从B和C转换声明归因于不同的进化而不是有意识的变化。

我未能找到任何有关BCPL的历史着作,但Ritchie的评论表明break或多或少是历史性事故。 BCPL后来解决了这个问题,但也许Ritchie和Thompson太忙于发明Unix而不愿意接受这样的细节: - )

答案 9 :(得分:3)

Java源自C,其遗产包括一种称为Duff's Device的技术。 这是一种优化,它依赖于在没有break;语句的情况下控制从一个案例落到下一个案例的事实。在C标准化的时候,有很多代码就像“野外”一样,改变语言以打破这种结构会适得其反。

答案 10 :(得分:1)

正如人们之前所说的,它是允许堕落而不是错误,这是一个特征。 如果有太多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

正如所料。

答案 11 :(得分:0)

因为有些情况下你想要流过第一个块,例如为了避免在多个块中编写相同的代码,但仍然能够将它们分开以进行mroe控制。还有很多其他原因。

答案 12 :(得分:0)

这是一个老问题,但实际上我今天遇到了没有破坏声明的情况。当你需要按顺序组合不同的函数时,不使用break实际上非常有用。

e.g。使用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.

答案 13 :(得分: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;
     }
 }

答案 14 :(得分:0)

我现在在我的switch语句中处理需要break的项目,否则代码将无法正常工作。请耐心等待,我将为您提供一个很好的示例,说明您在switch语句中需要break的原因。

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

在这种情况下,你有:

  1. State1 - 等待用户输入数字
  2. State2 - 打印总和
  3. state3 - 计算总和
  4. 查看各州,您希望exaction的顺序从 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,它将按此顺序执行, state1 state2 state3 。但是使用break,我们可以避免这种情况,并且可以在正确的过程中进行排序,该过程以state1开始,然后是state3,最后但最重要的是state2。

答案 15 :(得分:0)

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

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

答案 16 :(得分:-1)

确实,因为有了一些聪明的位置,你可以级联执行块。

相关问题