Java如何知道在打破循环时跳转到哪里?

Und*_*Dog 18 java loops compilation

while (condition) {

    if (condition) {
        statement1;
        statement2;

        break;
    } else {
        statement3;
        statement4;
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

jas*_*son 32

我不明白break语句如何"知道"它是在一个循环内,它首先退出.

break语句不知道它在一个switch或循环语句中.编译器验证break语句是否在switch循环语句中.如果遇到一个break是声明不是一个循环语句中,它会发出一个编译时错误.

如果没有switch,while,do,或者for直接封闭方法,构造函数或初始化程序语句包含break语句,就会发生编译时错误.

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

从而:

for(int i = 0; i < 10; i++) {
    if(i % 2 == 0) {
         break;
    }
}
Run Code Online (Sandbox Code Playgroud)

将被编译器翻译成:

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
Run Code Online (Sandbox Code Playgroud)

  • 在类路径中使用`Foo.class`,`javap -c Foo`将输出字节码. (12认同)
  • 你用什么来生成编译器代码.这对理解一些概念肯定有很大帮助. (2认同)
  • 谢谢杰森.欣赏它. (2认同)
  • @JNL:你必须原谅我,我在之前的评论中交换了一个'p`和`c`.我编辑纠正了! (2认同)

ζ--*_*ζ-- 20

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

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

return 这是一个功能块,虽然有一些差异,因为它可能需要携带指向堆的值或引用.


1 - 实际上比这复杂一点.适用于所有Java循环的最简单但准确的"模型"可能continue相当于在循环体末端跳转到虚构的语句.


use*_*421 17

我不明白break语句如何"知道"它是在一个循环内,它首先退出.

编译器将您的程序转换为解析树.除根之外,解析树中的所有内容都有父节点.break语句必须在树的某个地方有一个父循环(当然,还有父switch语句).

  • EJP - 这是一个很好的行`break语句必须有一个父循环`简单地显示了如何实现break.一个非常简洁和美好的答案! (2认同)
  • 这是这个问题最直接的答案.我喜欢字节码答案的细节,但编译器从代码构建的树是它知道生成字节码的方式. (2认同)