Java SE 6 技術手冊

3.4 流程控制

現實生活中待解決的事千奇百怪,在電腦發明之後,想要使用電腦解決的需求也是各式各樣:「如果」發生了…,就要…;「對於」…,就一直執行…;「如果」…,就「中斷」…。為了告訴電腦在特定條件下該執行的動作,您要會使用各種條件式來定義程式執行的流程。

3.4.1 if 條件式

為了應付「如果」發生了 …,就要 … 的需要,Java 提供了 if 條件式 ,它的語法如下:

if(條件式)
    陳述句一;
else
    陳述句二;

這個語法的意思,白話來說,就是當「條件式」成立時(true),則執行「陳述句一」,要不然(else)就執行「陳述句二」;如果條件式不成立時並不想作任何事,則 else 可以省略。

在 if 後如果有兩個以上陳述句,稱之為「複合陳述句」(Compound statement),此時必須使用 { } 定義區塊(Block)將複合陳述句包括起來,例如:

if(條件式) {
    陳述句一;
    陳述句二;
 }
 else {
    陳述句三;
    陳述句四;
 }

範例 3.25 是個簡單的程式,使用if條件式來判斷使用者的輸入是奇數還是偶數。

範例 3.25 OddDecider3.java

import java.util.Scanner;

public class OddDecider3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("請輸入數字: "); 
        int input = scanner.nextInt();
        int remain = input % 2; // 求除 2 的餘數

        if(remain == 1) // 如果餘數為1
            System.out.println(input + "為奇數"); 
        else 
            System.out.println(input + "為偶數"); 
    }
}

在 if 中您也可以再設定執行的條件,例如:

if(條件式一) { 
    陳述句一; 
    if(條件式二) 
        陳述句二; 
    陳述句三; 
 } 

這只個簡單的例子,其中陳述句二要執行,必須同時滿足「條件式一」與「條件式二」才行;再來看個例子:

if(條件式一) { 
    陳述句一; 
    // 其它陳述句 
 } 
 else 
    if(條件式二) 
        陳述句二; 

如果「條件式一」不滿足,就會執行 else 中的陳述,而您在這邊進行「條件式二」的測試,如果滿足就執行「陳述句二」,由於 Java 是個自由格式語言,您可以適當的排列這個程式,這會比較好懂一些:

if(條件式一) { 
    陳述句一; 
    // 其它陳述句 
} 
else if(條件式二) 
    陳述句二; 

基於這個方式,您可以如下設定多個條件,且易讀易懂:

 if(條件式一) 
    陳述一; 
 else if(條件式二) 
    陳述句二; 
 else if(條件式三) 
    陳述句三; 
 else 
        陳述句四; 

「陳述句四」會在條件式一、二、三都不成立時執行;範例3.26是個簡單的例子,處理學生的成績等級問題。

範例3.26 ScoreLevel.java

import java.util.Scanner;

public class ScoreLevel {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("輸入分數:"); 
        int score = scanner.nextInt();

        if(score >= 90) 
            System.out.println("得A"); 
        else if(score >= 80 && score < 90) 
            System.out.println("得B"); 
        else if(score >= 70 && score < 80) 
            System.out.println("得C"); 
        else if(score >= 60 && score < 70) 
            System.out.println("得D"); 
        else 
            System.out.println("得E(不及格)"); 
    }
}

執行結果如下:

輸入分數:88
得B

在這邊要注意的是 if 與 else 的配對問題,例如以下程式依縮排來看,您覺得有無問題存在?

if(條件式一) 
    if(條件式二) 
        陳述句一; 
    else 
        陳述句二; 

很多人都會以為「條件式二」的 if 會與 else 配對,但事實上是「條件式一」的 if 與 else 配對,加上括號的話就不會誤會了:

if(條件式一) { 
    if(條件式二) 
        陳述句一; 
    else 
        陳述句二; 
 } 

如果想避免這種錯誤,在程式中多使用括號是必要的,多寫一些總是比少寫一些來得保險一點。

注意到了嗎?在撰寫程式時適當的使用縮排(Indent),可以在定義程式區塊時讓區塊範圍看來容易分辨。

每個開發人員使用縮排的方式各不相同,您可以使用兩個空白或是四個空白,也可以按下Tab鍵來進行縮排。

良葛格的話匣子 我習慣的縮排方式是按四次空白鍵,不習慣按 Tab 鍵,因為各個文字編輯器或 IDE 對於 Tab 字元的顯示方式都不太一樣,有些文字編輯器或 IDE 預設使用 Tab 字元來自動縮排的話,我都還會將之改為預設四個空白字元進行縮排,因為空白字元顯示方式是一致的。

3.4.2 switch 條件式

switch 只能比較數值或字元,不過別以為這樣它就比 if 來得沒用,使用適當的話,它可比 if 判斷式來得有效率;switch 的語法架構如下:

switch(變數名稱或運算式) { 
    case 符合數字或字元: 
        陳述句一; 
        break; 
    case 符合數字或字元: 
        陳述句二; 
        break; 
    default: 
        陳述三; 
 } 

首先看看 switch 的括號,當中置放您要取出數值的變數,取出數值之後,程式會開始與 case 中所設定的數字或字元作比對,如果符合就執行當中的陳述句,直到遇到 break 後離開 switch 區塊,如果沒有符合的數值或字元,則會執行 default 後的陳述句,default 不一定需要,如果沒有預設要處理的動作,您可以省去這個部份。

來看看範例 3.26 的成績等級比對如何使用 switch 來改寫。

範例 3.27 ScoreLevel2.java

import java.util.Scanner;

public class ScoreLevel2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("請輸入分數: "); 
        int score = scanner.nextInt();
        int level = (int) score/10; 

        switch(level) { 
            case 10: 
            case 9: 
                System.out.println("得A"); 
                break; 
            case 8: 
                System.out.println("得B"); 
                break; 
            case 7: 
                System.out.println("得C"); 
                break; 
            case 6: 
                System.out.println("得D"); 
                break; 
            default: 
                System.out.println("得E(不及格)"); 
        }
    }
}

在這個程式中,您使用除法並取得運算後的商數,如果大於 90 的話,除以 10 的商數一定是 9 或 10(100 分時),在 case 10 中沒有任何的陳述,也沒有使用 break,所以會繼續往下執行,直到遇到 break 離開 switch 為止,所以學生成績 100 分的話,也會顯示 A 的成績等級;如果比對的條件不在10到6這些值的話,會執行 default 下的陳述,這表示商數小於 6,所以學生的成績等級就顯示為 E 了。

注意在 case 後的等號是冒號而不是分號,這是個很常打錯的符號;如果您比對的是字元,則記得加上單引號(' '),例如:

case 'A':

這個程式與使用 if 來判斷成績等級的程式有何不同?如果純綷比對數字或字元的話,建議使用 switch,因為它只會在一開始的 switch 括號中取出變數值一次,然後將這個值與下面所設定的 case 比對,但如果您使用if的話,每次遇到條件式時,都要取出變數值,效率的差異就在這,例如:

if(a == 1) 
    // .... 
else if(a == 2) 
    // .... 
else if(a == 3) 
    // .... 

這個程式片段在最差的狀況下,也就是 a = 3 時,共需三次比對,而每次比對都必須取出變數 a 的值一次,如果換成 switch 的話:

switch(a) { 
    case 1: 
        // .. 
        break; 
    case 2: 
        // .. 
        break; 
    case 3: 
        // .. 
        break; 
} 

在這個程式片段中,您只在一開頭 switch 的括號中取出變數 a 的值,然後逐一比對下面的 case,效率的差別就在於這邊;當然並不是使用 if 就不好,遇到複合條件時,switch 就幫不上忙了,您無法在 switch 中組合複雜的條件陳述,這時就得使用 if 了,簡單的說,if 與 switch 兩者可以搭配著靈活使用。

3.4.3 for 迴圈

在 Java 中如果要進行重複性的指令執行,可以使用 for 迴圈式,它的基本語法如下:

for(初始式; 判斷式; 遞增式) {
    陳述句一; 
    陳述句二; 
 }

如果陳述句只有一個,也就是非複合陳述句,{ } 可以省略不寫;for 迴圈的「第一個初始陳述句只會執行一次」,之後每次重新進行迴圈時,都會「根據判斷式來判斷是否執行下一個迴圈」,而每次執行完迴圈之後,都會「執行遞增式」。

實際看看範例 3.28 來瞭解 for 迴圈的功能。

範例3.28 SimpleLoopFor.java

public class SimpleLoopFor { 
    public static void main(String[] args) { 
        for(int i = 0; i < 10; i++) 
            System.out.print(" " + i); 
    } 
}

執行結果:

 0 1 2 3 4 5 6 7 8 9 

這是一個簡單的例子,但說明 for 的作用再適合不過,在 Java 中您可以直接在 for 中宣告變數與指定初始值,這個宣告的變數在 for 迴圈結束之後也會自動消失;初始變數的陳述句只被執行一次,接下來迴圈會根據 i 是否小於 10 來判斷是否執行迴圈,而每執行一次迴圈就將 i 加 1。

for 迴圈中也可以再使用 for 迴圈,初學者很喜歡用的例子就是顯示九九乘法表,這邊就用這個例子來說明:

範例3.29 NineTable.java

public class NineTable {
    public static void main(String[] args) {
        for(int j = 1; j < 10; j++) { 
           for(int i = 2; i < 10; i++) { 
               System.out.printf("%d*%d=%2d ",i, j,  i * j);
           } 
           System.out.println(); 
        }  
    }
}

執行結果:

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

事實上,for 迴圈的語法其實只是將三個複合陳述區塊寫在一個括號中而已,所不同的是第一個陳述區塊只會執行一次,第二個陳述區塊專司判斷是否繼續下一個迴圈,而第三個陳述區塊只是一般的陳述句,而不一定只放遞增運算式。

for 括號中的每個陳述區塊是以分號 ';' 作區隔,而在一個陳述區塊中若想寫兩個以上的陳述句,則使用逗號 ',' 作區隔,有興趣的話,看看範例 3.30 中九九乘法表的寫法,它只使用了一個 for 迴圈就可以完成九九乘法表的列印,執行結果與範例 3.29 是一樣的。

範例 3.30 NineTable2.java

public class NineTable2 { 
    public static void main(String[] args) {
        for (int i = 2, j = 1; 
             j < 10; 
             i = (i==9)?((++j/j)+1):(i+1)) { 
            System.out.printf("%d*%d=%2d%c", i, j,  i * j,
                                 (i==9 ? '\n' : ' '));
        }   
    }
}

這個程式的重點在於讓您看看 for 迴圈的括號中之寫法,當然原則上並不鼓勵這麼寫,程式基本上還是以清楚易懂為原則,至於這個九九乘法表的演算其實也不難,當中的語法之前都介紹過了,您可以親自演算看看就知道怎麼回事了。

3.4.4 while 迴圈

Java 提供 while 迴圈式,它根據您所指定的條件式來判斷是否執行迴圈本體,語法如下所示:

while(條件式) {
    陳述句一; 
    陳述句二; 
} 

如果迴圈本體只有一個陳述句,則 while 的 { } 可以省略不寫;while 像是沒有起始陳述與終止陳述的 for 迴圈,主要用於重複性的動作,而停止條件未知何時發生的情況,例如一個使用者輸入介面,使用者可能輸入 10 次,也可能輸入 20 次,這時迴圈停止的時機是未知的,您可以使用 while 迴圈來作這個事。

一個計算輸入成績平均的程式如範例 3.31 所示。

範例 3.31 ScoreAverage.java

import java.util.Scanner;

public class ScoreAverage { 
    public static void main(String[] args) { 
        Scanner scanner = new Scanner(System.in);
        int score = 0; 
        int sum = 0; 
        int count = -1; 

        while(score != -1) { 
            count++; 
            sum += score; 
            System.out.print("輸入分數(-1結束):"); 
            score = scanner.nextInt();
        } 

        System.out.println("平均:" + (double) sum/count); 
    } 
}

在這個程式中,使用者的輸入次數是未知的,所以您使用 while 迴圈來判斷使用者的輸入是否為 -1,以作為迴圈執行的條件,一個執行的例子如下:

輸入分數(-1結束):99
輸入分數(-1結束):88
輸入分數(-1結束):77
輸入分數(-1結束):-1
平均:88.0

while 可以用作無窮迴圈,很多地方都用到的到無窮迴圈,例如遊戲設計中對使用者輸入裝置的輪詢(poll),或是動畫程式的播放都會使用到無窮廻圈,一個無窮迴圈如下所示:

while(true) { 
    迴圈內容; 
    .... 
 }

無窮迴圈可以由自己迴圈中的某個條件式來結束,例如下面是一個迴圈內部終止的例子:

while(true) { 
    陳述句; 
    if(條件式) 
        break;  // 跳離迴圈 
     .... 
} 

當「條件式」成立時,會執行 break 離開 while 迴圈,這個 break 與 switch 中的作用是一樣的,都是要離開當時執行的程式區塊時使用。

while 迴圈有時稱之為「前測式迴圈」,因為它在迴圈執行前就會進行條件判斷,而另一個 do while 稱之「後測式迴圈」,它會先執行迴圈本體,然後再進行條件判斷,do while 的語法如下所示:

do { 
    陳述句一; 
    陳述句二; 
    .... 
 } while(條件式);

注意 while 後面是以分號 ';'作為結束,這個很常被忽略;由於 do while 會先執行迴圈,所以它通常用於進行一些初始化或介面溝通的動作,例如範例 3.32 所示。

範例 3.32 OddDecider4.java

import java.util.Scanner;

public class OddDecider4 { 
    public static void main(String[] args) { 
        Scanner scanner = new Scanner(System.in);
        int input = 0;
        int replay = 0; 

        do { 
            System.out.print("輸入整數值:"); 
            input = scanner.nextInt();
            System.out.println("輸入數為奇數?" + 
                            ((input%2 == 1) ? 'Y': 'N')); 
            System.out.print("繼續(1:繼續 0:結束)?"); 
            replay = scanner.nextInt();
        } while(replay == 1);   
    } 
}

執行結果:

輸入整數值:77
輸入數為奇數?Y
繼續(1:繼續 0:結束)?0

3.4.5 break、continue

break 可以離開目前 switch、for、while、do while 的區塊,並前進至區塊後下一個陳述句,在 switch 中主要用來中斷下一個 case 的比對,在 for、while與do while 中,主要用於中斷目前的迴圈執行,break 的例子您之前已經看過不少,這邊不再舉例。

continue 的作用與 break 類似,主要使用於迴圈,所不同的是 break 會結束區塊的執行,而 continue 只會結束其之後區塊的陳述句,並跳回迴圈區塊的開頭繼續下一個迴圈,而不是離開迴圈,例如:

for(int i = 1; i < 10; i++) { 
    if(i == 5) 
        break; 

    System.out.println("i = " + i); 
 }

這段程式會顯示 i = 1 到 i = 4,因為當 i 等於 5 時就會執行 break 而離開迴圈,再看下面這個程式:

for(int i = 1; i < 10; i++) { 
    if(i == 5) 
        continue; 

    System.out.println("i = " + i); 
} 

這段程式會顯示 1 到 4,與 6 到 9,當 i 等於 5 時,會執行 continue 直接結束此次迴圈,這次迴圈中 System.out.println() 該行並沒有被執行,然後從區塊開頭執行下一次迴圈,所以 5 並沒有被顯示。

break 與 continue 還可以配合標籤使用,例如本來 break 只會離開 for 迴圈,設定標籤與區塊,則可以離開整個區塊,範例 3.33 舉個簡單的示範。

範例 3.33 BreakTest.java

public class BreakTest { 
    public static void main(String[] args) { 
        back : { 
            for(int i = 0; i < 10; i++) { 
                if(i == 9) { 
                    System.out.println("break"); 
                    break back; 
                } 
            } 
            System.out.println("test"); 
        } 
    } 
}

程式的執行結果會顯示 break;back 是個標籤,當 break back; 時,返回至 back 標籤處,之後整個 back 區塊不執行而跳過,所以這個程式 System.out.println("test"); 不會被執行。

事實上 continue 也有類似的用法,只不過標籤只能設定在 for 之前,範例 3.34 舉個簡單的示範。

範例3.34 ContinueTest.java

public class ContinueTest { 
    public static void main(String[] args) { 
        back1: 
        for(int i = 0; i < 10; i++){ 
            back2: 
            for(int j = 0; j < 10; j++) { 
                if(j == 9) { 
                    continue back1; 
                } 
            }
            System.out.println("test"); 
        } 
    } 
}

continue 配合標籤,可以自由的跳至任何一層 for 迴圈,您可以試試 continue back1 與 continue back2 的不同,設定 back1 時,System.out.println("test"); 不會被執行。