为什么我们需要在 case 语句之后break?

发布于 2024-08-30 09:05:57 字数 68 浏览 15 评论 0原文

为什么编译器不自动在 switch 中的每个代码块后面放置break语句?是因为历史原因吗?你什么时候想要执行多个代码块?

Why doesn't the compiler automatically put break statements after each code block in the switch? Is it for historical reasons? When would you want multiple code blocks to execute?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

番薯 2024-09-06 09:05:57

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

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

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

等。仅举一个例子。

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

Sometimes it is helpful to have multiple cases associated with the same code block, such as

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

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

etc. Just an example.

In my experience, usually it is bad style to "fall through" and have multiple blocks of code execute for one case, but there may be uses for it in some situations.

醉生梦死 2024-09-06 09:05:57

历史上,这是因为 case 本质上定义了一个标签,也称为 goto 的目标点调用。 switch 语句及其关联的情况实际上只是表示一个多路分支,具有代码流中的多个潜在入口点。

话虽如此,人们已经无数次注意到,break 几乎总是您希望在每种情况结束时都采用的默认行为。

Historically, it's because the case was essentially defining a label, also known as the target point of a goto call. The switch statement and its associated cases really just represent a multiway branch with multiple potential entry points into a stream of code.

All that said, it has been noted a nearly infinite number of times that break is almost always the default behavior that you'd rather have at the end of every case.

丢了幸福的猪 2024-09-06 09:05:57

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

Java comes from C and that is the syntax from C.

There are times where you want multiple case statements to just have one execution path.
Below is a sample that will tell you how many days in a month.

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);
    }
}
笑咖 2024-09-06 09:05:57

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

例如,假设您想要对所有情况执行特定操作,但在特定情况下您想要执行该操作以及其他操作。使用带有 drop-through 的 switch 语句会使事情变得非常容易。

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

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

You can do all sorts of interesting things with case fall-through.

For example, lets say you want to do a particular action for all cases, but in a certain case you want to do that action plus something else. Using a switch statement with fall-through would make it quite easy.

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

Of course, it is easy to forget the break statement at the end of a case and cause unexpected behavior. Good compilers will warn you when you omit the break statement.

友谊不毕业 2024-09-06 09:05:57

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

通过这些更改,可以避免每个开关 casebreak,如进一步演示的:-

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

On 使用 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

The break after switch cases is used to avoid the fallthrough in the switch statements. Though interestingly this now can be achieved through the newly formed switch labels as implemented via JEP-325.

With these changes, the break with every switch case can be avoided as demonstrated further :-

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

On executing the above code with JDK-12, the comparative output could be seen as

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

and

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

and of course the thing unchanged

// input
3
many // default case match
many // branches to 'default' as well
扶醉桌前 2024-09-06 09:05:57

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

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

是因为历史原因吗?您什么时候想要执行多个代码块?

它主要是为了与 C 兼容,可以说是很久以前 goto 关键字在地球上流行的一种古老的技巧。当然,它确实实现了一些令人惊奇的事情,例如Duff 的设备 ,但无论这是赞成还是反对,充其量只是争论而已。

Why doesn't the compiler automatically put break statements after each code block in the switch?

Leaving aside the good desire to be able to use the identical block for several cases (which could be special-cased)...

Is it for historical reasons? When would you want multiple code blocks to execute?

It's mainly for compatibility with C, and is arguably an ancient hack from the days of old when goto keywords roamed the earth. It does enable some amazing things, of course, such as Duff's Device, but whether that's a point in its favor or against is… argumentative at best.

不喜欢何必死缠烂打 2024-09-06 09:05:57

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

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

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

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

以级联方式。

如果你问我的话,真的很容易出现错误/混乱。

So you do not have to repeat code if you need several cases to do the same thing:

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

Or you can do things like :

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

In a cascade fashion.

Really bug/confusion prone, if you ask me.

彡翼 2024-09-06 09:05:57

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

例如,从 BCPL switchon 语句中转义的 endcase 在该语言中不存在
当我们在 20 世纪 60 年代学习它时,因此重载了break关键字来转义
B 和 C 的 switch 语句归因于发散进化而不是有意识的变化。

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

As far as the historical record goes, Tony Hoare invented the case statement in the 1960s, during the "structured programming" revolution. Tony's case statement supported multiple labels per case and automatic exit with no stinking break statements. The requirement for an explicit break was something that came out of the BCPL/B/C line. Dennis Ritchie writes (in ACM HOPL-II):

For example, the endcase that escapes from a BCPL switchon statement was not present in the language
when we learned it in the 1960s, and so the overloading of the break keyword to escape
from the B and C switch statement owes to divergent evolution rather than conscious change.

I haven't been able to find any historical writings about BCPL, but Ritchie's comment suggests that the break was more or less a historical accident. BCPL later fixed the problem, but perhaps Ritchie and Thompson were too busy inventing Unix to be bothered with such a detail :-)

始终不够 2024-09-06 09:05:57

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

Java is derived from C, whose heritage includes a technique known as Duff's Device .
It's an optimization that relies on the fact that control falls through from one case to the next, in the absence of a break; statement. By the time C was standardized, there was plenty of code like that "in the wild", and it would have been counterproductive to change the language to break such constructions.

淡看悲欢离合 2024-09-06 09:05:57

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

如预期。

As people said before, it is to allow fall-through and it is not a mistake, it is a feature.
If too many break statements annoy you, you can easily get rid of them by using return statements instead. This is actually a good practice, because your methods should be as small as possible (for the sake of readability and maintainability), so a switch statement is already big enough for a method, hence, a good method should not contain anything else, this is an example:

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

The execution prints:

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

as expected.

深者入戏 2024-09-06 09:05:57

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

例如,使用http响应代码通过时间令牌服务器响应代码401来验证用户

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

in case 语句:

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 响应调用 log in user 函数,因为重新生成令牌时,运行时会跳转到 case 200。

It is an old question but actually I ran into using the case without break statement today. Not using break is actually very useful when you need to combine different functions in sequence.

e.g. using http response codes to authenticate user with time token

server response code 401 - token is outdated -> regenerate token and log user in.
server response code 200 - token is OK -> log user in.

in case statements:

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

Using this you do not need to call log in user function for 401 response because when the token is regenerated, the runtime jumps into the case 200.

怪我太投入 2024-09-06 09:05:57

如果编译器没有添加自动中断,则可以通过从 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;
}

Not having an automatic break added by the compiler makes it possible to use a switch/case to test for conditions like 1 <= a <= 3 by removing the break statement from 1 and 2.

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;
}
百思不得你姐 2024-09-06 09:05:57

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

because there are situations where you want to flow through the first block for example to avoid writing the same code in multiple blocks but still be able to divide them for mroe control. There are also a ton of other reasons.

翻了热茶 2024-09-06 09:05:57

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

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

You can makes easily to separate other type of number, month, count.
This is better then if in this case;

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;
     }
 }
娜些时光,永不杰束 2024-09-06 09:05:57

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

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

在这种情况下,您可以:

  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。

I am now working on project where I am in need of break in my switch statement otherwise the code won't work. Bear with me and I will give you a good example of why you need break in your switch statement.

Imagine you have three states, one that waits for the user to enter a number, the second to calculate it and the third to print the sum.

In that case you have:

  1. State1 - Wait for user to enter a number
  2. State2 - Print the sum
  3. state3 - Calculate the sum

Looking at the states, you would want the order of exaction to start on state1, then state3 and finally state2. Otherwise we will only print users input without calculating the sum. Just to clarify it again, we wait for the user to enter a value, then calculate the sum and prints the sum.

Here is an example code:

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

If we don't use break, it will execute in this order, state1, state2 and state3. But using break, we avoid this scenario, and can order in the right procedure which is to begin with state1, then state3 and last but not least state2.

遥远的她 2024-09-06 09:05:57

确实如此,因为通过一些巧妙的放置,您可以级联执行块。

Exactly, because with some clever placement you can execute blocks in cascade.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文