2013-07-23 28 views
18
while (condition) { 

    if (condition) { 
     statement1; 
     statement2; 

     break; 
    } else { 
     statement3; 
     statement4; 
    } 

} 

通過在if子句中使用break,我們確保循環被暫停並退出。Java如何知道何時跳出循環?

我不明白break語句是如何「知道」它是在一個循環中,以便它首先退出,或者它如何「知道」跳轉到哪裏。這是如何發生的?

+11

編譯器知道其中環是。 –

+3

@HotLicks編譯器應該是無所不知的:) – hexafraction

回答

32

我不明白break語句是如何「知道」它是在一個循環內部才能退出的。

break聲明不知道它在switch或循環語句中。編譯器驗證break語句在switch或循環語句內。如果在循環語句中遇到break語句而不是,它將發出編譯時錯誤。

如果沒有直接封閉方法,構造函數或初始化程序switchwhiledo,或for語句包含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 
+2

你用什麼來生成編譯器代碼。這對於理解一些概念肯定有幫助。 – JNL

+12

在類路徑中使用'Foo.class','javap -c Foo'會輸出字節碼。 – jason

+2

謝謝傑森。欣賞它。 – JNL

20

break不是您的標準功能。它是Java編譯器使用的關鍵字。當它看到它時,它會插入一個字節碼指令,直接跳轉到循環之外。這是一個簡單的goto字節碼,如Jason給出的答案所示。

同樣,continue關鍵字有效跳轉到循環開始。

return這是從一個功能塊中完成的,但有一些差異,因爲它可能需要攜帶一個指向堆的值或引用。


1 - 它實際上比這更復雜一點。可能是適用於所有Java循環的最佳簡單但精確的「模型」,continue相當於在循環體的末尾跳轉到假想的語句。

+0

堆棧不能'清理'。沒有對應於關閉的字節碼指令}。 – EJP

17

我不明白break語句是如何「知道」它是在一個循環中,以便它首先退出。

編譯器將您的程序轉換爲分析樹。解析樹中的所有內容都有一個父級,除了根外。 break語句必須在樹的某個位置有一個父循環(或者,當然還有一個父切換語句)。

+2

EJP - 這是一條絕妙的線路「break語句必須有一個父循環」只是顯示瞭如何實現break。一個非常簡潔和不錯的答案! –

+2

這是對問題最直接的回答。我喜歡字節碼答案的細節,但編譯器從代碼構建的樹是它知道如何生成該字節碼的。 –

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 

這可能是(非常糟糕)字節碼for:

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

基本上,在較低級別上,您使用標籤/行號和gotos來跳轉代碼。所以一個break本質上意味着,轉到if語句或循環的最後一行。

4

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

希望這會有所幫助。

+1

這是有效的解釋什麼是休息,但不是如何在低水平,如OP所要求的那樣。 – hexafraction

+0

@hexafraction True。我昨天回答了這個問題,並在稍後編輯了這個問題。因爲我們有很多答案,但我沒有提到它,所以我只想把它變成光標和無標籤的break語句。這肯定會幫助編程新手 – JNL