【问题标题】:How does Java know where to jump to when breaking out of a loop?Java如何知道跳出循环时跳转到哪里?
【发布时间】:2013-07-23 02:00:20
【问题描述】:
while (condition) {

    if (condition) {
        statement1;
        statement2;

        break;
    } else {
        statement3;
        statement4;
    }

}

通过在 if 子句中使用break,我们确保循环停止并退出。

我不明白 break 语句是如何“知道”它在循环中首先退出的,或者它是如何“知道”跳转到哪里的。这是怎么发生的?

【问题讨论】:

  • 编译器知道循环在哪里。
  • @HotLicks 编译器应该是无所不知的:)

标签: java loops compilation


【解决方案1】:

我不明白 break 语句是如何“知道”它在循环中首先退出的。

break 语句不知道它在switch 或循环语句中。编译器验证 break 语句在switch 或循环语句中。如果在循环语句中遇到 notbreak 语句,它将发出编译时错误。

如果直接封闭的方法、构造函数或初始化程序中的 switchwhiledofor 语句不包含 break 语句,则会发生编译时错误。

如果编译器能够验证break 语句在switch 或循环语句中,它将发出 JVM 指令以立即跳转到最近的封闭循环之后的第一条语句。

因此:

for(int i = 0; i < 10; i++) {
    if(i % 2 == 0) {
         break;
    }
}

会被编译器翻译成:

0:  iconst_0        # push integer 0 onto stack
1:  istore_1        # store top of stack in local 1 as integer                  
                    # i = 0
2:  iload_1         # push integer in local 1 onto stack
3:  bipush 10       # push integer 10 onto stack
5:  if_icmpge 23    # pop and compare top two (as integers), jump if first >= second
                    # if i >= 10, end for
8:  iload_1         # push integer in local 1 onto stack
9:  iconst_2        # push integer 2 onto stack
10: irem            # pop top two and computes first % second and pushes result
                    # i % 2
11: ifne 17         # pop top (as integer) and jump if not zero to 17
                    # if(i % 2 == 0) 
14: goto 23         # this is the break statement
17: iinc 1, 1       # increment local 1 by 1
                    # i++
20: goto 2          # go to top of loop
                    # loop
23: return          # end of loop body

【讨论】:

  • 你用什么来生成编译器代码。这对理解一些概念肯定有很大帮助。
  • 类路径中有Foo.classjavap -c Foo 将输出字节码。
  • 谢谢杰森。欣赏它。
  • @JNL:请原谅我,我在之前的评论中交换了pc。我已经编辑更正了!
  • +1 表示低级代码,所以如果我有myc.calss 那么javap -c myc.calss 将输出此代码?
【解决方案2】:

break 不是您的标准功能。它是 Java 编译器使用的关键字。当它看到它时,它会在它之后插入一条字节码指令直接跳转到循环之外。这是一个简单的 goto 字节码,如 Jason 给出的答案所示。

同样,continue 关键字有效地跳转到循环的开头1

return 在功能块之外执行此操作,但有一些区别,因为它可能需要携带一个值或指向堆的引用。


1 - 实际上比这要复杂一些。可能适用于所有 Java 循环的最简单但准确的“模型”是 continue 相当于在循环体末尾跳转到一个虚构的 empty 语句。

【讨论】:

  • 无法“清理”堆栈。没有与结束 } 对应的字节码指令。
【解决方案3】:

我不明白 break 语句是如何“知道”它在循环中的,以便它首先退出。

编译器将您的程序变成解析树。解析树中的所有内容都有一个父级,除了根。 break 语句必须在树的某处有一个父循环(或者,当然,一个父 switch 语句)。

【讨论】:

  • EJP -- 这是一个很好的行The break statement must have a parent loop 简单地展示了break 是如何实现的。一个非常简洁和好的答案!
  • 这是对这个问题最直接的回答。我喜欢字节码答案的细节,但是编译器从代码构建的树是它知道生成该字节码的方式。
【解决方案4】:

如果您曾经看过汇编或 Java 字节码,这将更有意义。在较低级别,您的程序被编译为利用寄存器、地址等的“字节码”。一个简单的 if 语句可以翻译成如下内容:

3: if_icmpeq 5
4: goto 10
5: iconst_1
6: iload_1
7: iconst_2
8: iload_2
9: if_icmpeq 10
10: // end of if-else statement

这可能是(非常糟糕的)字节码:

if ( x == y )
  if ( 1 == 2 )

本质上,在较低级别,您使用标签/行号和 goto 来跳转代码。所以 break 本质上意味着,转到 if 语句或循环的最后一行。

【讨论】:

    【解决方案5】:

    break 语句有两种形式:有标签和无标签

    您可以使用未标记的中断来终止for, while, or do-while loop.

    未标记的 break 语句会终止 innermost switch, for, while, or do-while statement,但会终止 labeled break terminates an outer statement

       search:
            for (i = 0; i < arrayOfInts.length; i++) {
                for (j = 0; j < arrayOfInts[i].length;
                     j++) {
                    if (arrayOfInts[i][j] == 5) {
                        foundIt = true;
                        break search;
                    }
                }
            }
    

    希望这会有所帮助。

    【讨论】:

    • 这对于解释 break 的作用是有效的,但不能像 OP 所要求的那样在低级别解释它的作用。
    • @hexafraction 真。我昨天回答了这个问题,这个问题后来被编辑了。我也只是想把轻标签和无标签的 break 语句带入,因为我们有很多答案,但没有人提到它。这肯定会对刚接触编程的人有所帮助
    猜你喜欢
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    相关资源
    最近更新 更多