《Java從入門到失業》第三章:基礎語法及基本程序結構(3.8):流程控制(循環語句、while語句、for語句)
- 2020 年 9 月 8 日
- 筆記
- 《Java從入門到失業》, Java從入門到失業
3.8.2循環語句
3.8.2.1while語句
最近這些年買彩票只能去投注站買,早些年,筆者經常是在網上買。在網上買有個功能:追號。就是假如你想一直買同一組號碼,直到中大獎為止。你可以設置一個條件,比如中了頭獎就不繼續買了,如果沒有中頭獎,下一期繼續買同樣的號碼。對於這樣的功能,在程序中可以採用while循環來實現:
代碼如下:
while(n<5000000) { System.out.println("下一期繼續買同一組號碼"); }
但是事實上,我們先要買第一期,然後才能判斷是否中頭獎,循環才能繼續:
代碼如下:
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個部分放置任何表達式,並且都是可以省略不寫的。示意圖如下:
另外,對於在表達式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語句。
首先我們得想一個多層嵌套的例子,正當我冥思苦想的時候,突然發我兒子床頭的一張乘法口訣表:
假如我們用程序打印這張表,可以用到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