《Java從入門到失業》第三章:基礎語法及基本程式結構(3.8):流程控制(迴圈語句、while語句、for語句)

Java大失叔 發表於 2020-09-08

3.8.2迴圈語句

3.8.2.1while語句

       最近這些年買彩票只能去投注站買,早些年,筆者經常是在網上買。在網上買有個功能:追號。就是假如你想一直買同一組號碼,直到中大獎為止。你可以設定一個條件,比如中了頭獎就不繼續買了,如果沒有中頭獎,下一期繼續買同樣的號碼。對於這樣的功能,在程式中可以採用while迴圈來實現:

《Java從入門到失業》第三章:基礎語法及基本程式結構(3.8):流程控制(迴圈語句、while語句、for語句)

程式碼如下:

while(n<5000000) {  
    System.out.println("下一期繼續買同一組號碼");  
} 

但是事實上,我們先要買第一期,然後才能判斷是否中頭獎,迴圈才能繼續:

 《Java從入門到失業》第三章:基礎語法及基本程式結構(3.8):流程控制(迴圈語句、while語句、for語句)

程式碼如下:

do {  
    System.out.println("買一組號碼");  
} while (n < 5000000);  

下面我們再用一個示例來解決我們兒子最近學奧數的一個數學問題,計算1+2+3+…+100;

int sum = 0;// 最終結果,初始為0  
int add = 1;// 加數,初始為1  
while (add <= 100) {  
    sum = sum + add;  
    add++;// 加完後,自增1  
}  
System.out.println(sum);//最終結果是5050  

通過上面例項我們知道,當while的條件為真,則執行迴圈語句。如果這個條件一直為真的話,程式就會進入一個死迴圈了。因此在實際程式編寫的時候,一定要保證這個條件隨著程式的執行,會在某一個時刻變為假,避免程式進入死迴圈。比如上面這個例子變數add會自增,所以一定大於100。

3.8.2.2for語句

       對於上面這個數學問題,我們可以看出來,它的迴圈次數是固定的,對於這種迴圈問題,Java還有一種更加簡潔的語句來實現,就是for迴圈。程式碼可以寫成這樣:

int sum = 0;// 最終結果,初始為0  
for (int add = 1; add <= 100; add++) {  
    sum += add;  
}  
System.out.println(sum);// 最終結果是5050 

我們可以看到,程式碼的第2行,把加數add的初始化、迴圈條件和add的自增都放到一行了,顯得更加簡潔。它的通用結構如下:

for(表示式1 ; 表示式2 ; 表示式3)

  • 表示式1:一般用來初始化迴圈迭代計數器
  • 表示式2:必須是一個結果為boolean的表示式,一般用作迴圈條件
  • 表示式3:一般用來迭代迴圈計數器

       其實對於for迴圈中括號內,是分成3個部分,每個部分之間用分號(;)隔開。Java允許這3個部分放置任何表示式,並且都是可以省略不寫的。示意圖如下:

 《Java從入門到失業》第三章:基礎語法及基本程式結構(3.8):流程控制(迴圈語句、while語句、for語句)

另外,對於在表示式1中宣告的變數,它的作用域是整個for迴圈的迴圈體。對於在迴圈語句中定義的變數,作用域只能在迴圈體{}內。

       有的時候,在一個for迴圈中,會有多個計數器,例如前面追號買彩票的例子,可以設定追號10期,但是有的時候你的賬戶餘額不足了,彩票站不會給你墊錢追號的,程式碼可以寫成這樣:

for (int balance = 10, count = 1; balance >= 2 && count <= 10; count++, balance -= 2) {  
    System.out.println("餘額還剩" + balance + "元,購買第" + count + "期彩票");  
} 

balance:賬戶餘額;count:追號期數

執行結果:

餘額還剩10元,購買第1期彩票  
餘額還剩8元,購買第2期彩票  
餘額還剩6元,購買第3期彩票  
餘額還剩4元,購買第4期彩票  
餘額還剩2元,購買第5期彩票  

我們可以看到:

  • 表示式1可以同時定義多個同型別的變數(balance和count)
  • 表示式2迴圈條件是餘額大於等於2(夠一張彩票錢)並且追號期數小於等於10(我們設定的期數)
  • 表示式3可以同時對balance和count進行更新

雖然Java的語法規則對for迴圈的表示式1、表示式2、表示式3的限制非常少,但是筆者不建議編寫晦澀難懂的語句,儘量保證程式碼的可讀性。

3.8.2.3break

       在上面這個例子中,程式碼其實寫的就有點晦澀,不易閱讀。對於餘額不足的情況,其實可以認為是中止迴圈的一個條件。我們可以用break來中止迴圈,程式碼可以改寫:

int balance = 10;  
for (int count = 1; count <= 10; count++) {  
    System.out.println("餘額還剩" + balance + "元,購買第" + count + "期彩票");  
    balance -= 2;  
    // 當餘額不足2元的時候,中止迴圈  
    if (balance < 2) {  
        break;  
    }  
}  

在while中同樣可以使用break,我們將上面程式碼改寫成while版本:

int balance = 10;  
int count = 1;  
while (count <= 10) {  
    System.out.println("餘額還剩" + balance + "元,購買第" + count + "期彩票");  
    count++;  
    balance -= 2;  
    // 當餘額不足2元的時候,中止迴圈  
    if (balance < 2) {  
        break;  
    }  
}  

這2段程式碼的執行結果都是:

餘額還剩10元,購買第1期彩票  
餘額還剩8元,購買第2期彩票  
餘額還剩6元,購買第3期彩票  
餘額還剩4元,購買第4期彩票  
餘額還剩2元,購買第5期彩票  

break關鍵字,只能中止當前迴圈,當有多個迴圈巢狀使用的時候,有時候想要直接中止最外層迴圈,對於這種需求,在C++中是使用goto關鍵字來實現的。我們在學習關鍵字的時候,發現Java將goto作為保留字了,但是卻沒有使用它,而是用了另外一種方法來實現。叫做帶標籤的break語句。

       首先我們得想一個多層巢狀的例子,正當我冥思苦想的時候,突然發我兒子床頭的一張乘法口訣表:

 《Java從入門到失業》第三章:基礎語法及基本程式結構(3.8):流程控制(迴圈語句、while語句、for語句)

假如我們用程式列印這張表,可以用到2層巢狀的迴圈語句。第一層迴圈列印每一行的所有算式,然後我們把列印每一行的功能也用一個迴圈來實現即第二層迴圈。程式碼如下:

// row是行號,一共需要列印9行  
for (int row = 1; row <= 9; row++) {  
    // column是列號,對於第row行,一共需要列印row列  
    for (int column = 1; column <= row; column++) {  
        System.out.print(column + "×" + row + "=" + column * row + " ");  
    }  
    System.out.println();// 列印完一行,需要換行  
} 

執行結果如下:

1×1=1   
1×2=2 2×2=4   
1×3=3 2×3=6 3×3=9   
1×4=4 2×4=8 3×4=12 4×4=16   
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25   
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36   
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49   
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64   
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81

Perfect!完美的列印出來了。假如我們想在列印到第8行第5列的時候,不想列印了,程式碼改成下面這樣:

// row是行號,一共需要列印9行  
for (int row = 1; row <= 9; row++) {  
    // column是列號,對於第row行,一共需要列印row列  
    for (int column = 1; column <= row; column++) {  
        System.out.print(column + "×" + row + "=" + column * row + " ");  
        if (row == 8 && column == 5) {  
            break;  
        }  
    }  
  System.out.println();// 列印完一行,需要換行
}

但是這樣的結果如下:

1×1=1   
1×2=2 2×2=4   
1×3=3 2×3=6 3×3=9   
1×4=4 2×4=8 3×4=12 4×4=16   
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25   
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36   
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49   
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40   
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81   

雖然第8行第5列之後的沒有列印,但是第9行又列印出來了。我們再改成帶標籤的break。

 1 print_row: // 這是一個標籤  
 2 for (int row = 1; row <= 9; row++) {  
 3     // column是列號,對於第row行,一共需要列印row列  
 4     for (int column = 1; column <= row; column++) {  
 5         System.out.print(column + "×" + row + "=" + column * row + " ");  
 6         if (row == 8 && column == 5) {  
 7             break print_row;// 中止標籤print_row  
 8         }  
 9     }  
10     System.out.println();// 列印完一行,需要換行  
11 }  
 

執行結果:

1×1=1   
1×2=2 2×2=4   
1×3=3 2×3=6 3×3=9   
1×4=4 2×4=8 3×4=12 4×4=16   
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25   
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36   
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49   
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40   

這一次結果正確。我們在第1行新增一個標籤“print_row”,然後在第7行中止該標籤。這樣做相當於跳轉到標籤“print_row”標記的程式碼塊的末尾即第11行。需要注意的是,標籤後面需要緊跟一個冒號(:)。

3.8.2.4continue

       在上面列印乘法口訣表的例子,假如我們不想列印第4行和第4列,想想有啥辦法嗎?我們可以想到,當列印到第4行的時候,直接換一行去列印第5行。當列印到第4列的時候,也跳過,然後去列印第5列。對於這種需求,我們可以用到continue語句。continue的作用就是跳過當前迴圈體中剩餘的部分,回到當前迴圈的首部。程式碼如下:

 1 for (int row = 1; row <= 9; row++) {  
 2     /*第4行,列印換行,然後繼續列印下一行*/  
 3     if (row == 4) {  
 4         System.out.println();  
 5         continue;  
 6     }  
 7     for (int column = 1; column <= row; column++) {  
 8         /*第4列,打6個空格,然後繼續列印下一列*/  
 9         if (column == 4) {  
10             System.out.print("      ");  
11             continue;  
12         }  
13         System.out.print(column + "×" + row + "=" + column * row + " ");  
14     }  
15     System.out.println();  
16 }  

執行結果如下:

1×1=1   
1×2=2 2×2=4   
1×3=3 2×3=6 3×3=9   
  
1×5=5 2×5=10 3×5=15       5×5=25   
1×6=6 2×6=12 3×6=18       5×6=30 6×6=36   
1×7=7 2×7=14 3×7=21       5×7=35 6×7=42 7×7=49   
1×8=8 2×8=16 3×8=24       5×8=40 6×8=48 7×8=56 8×8=64   
1×9=9 2×9=18 3×9=27       5×9=45 6×9=54 7×9=63 8×9=72 9×9=81   

我們看到,結果第4行和第4列都空出來了。需要注意的是,continue只是跳過當前迴圈體的剩餘部分,如果是for迴圈,表示式3部分還是會執行的。

       continue語句也可以帶標籤,作用是跳到與標籤匹配的迴圈首部(如果是for迴圈,則是表示式3)。我們看程式碼和結果:

print_row:  
for (int row = 1; row <= 9; row++) {  
    for (int column = 1; column <= row; column++) {  
        /*第4列,則直接列印下一行*/  
        if (column == 4) {  
            System.out.println();  
            continue print_row;  
        }  
        System.out.print(column + "×" + row + "=" + column * row + " ");  
    }  
    System.out.println();  
}  
 

結果:

1×1=1   
1×2=2 2×2=4   
1×3=3 2×3=6 3×3=9   
1×4=4 2×4=8 3×4=12   
1×5=5 2×5=10 3×5=15   
1×6=6 2×6=12 3×6=18   
1×7=7 2×7=14 3×7=21   
1×8=8 2×8=16 3×8=24   
1×9=9 2×9=18 3×9=27