程式的靈魂在於流程控制。無論多麼複雜的演算法、商務邏輯、遊戲規則,最終都由三種基本結構組合而成:順序、選擇、迴圈。本章將系統解析 Java 的流程控制語句,從基礎語法到進階技巧、常見陷阱與最佳實踐,幫你建立堅實的邏輯思維基礎。
int score = 85;
if (score >= 60) {
System.out.println("及格");
}
// 輸出: 及格
關鍵點:條件必須是 boolean 型別;單行敘述可省略大括號,但強烈建議永遠加上大括號,避免維護時誤判歸屬。
int score = 45;
if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
// 輸出: 不及格
int score = 82;
if (score >= 90) {
System.out.println("優等 (A)");
} else if (score >= 80) {
System.out.println("甲等 (B)");
} else if (score >= 70) {
System.out.println("乙等 (C)");
} else if (score >= 60) {
System.out.println("丙等 (D)");
} else {
System.out.println("不及格 (F)");
}
// 輸出: 甲等 (B)
執行機制:由上而下評估,第一個符合條件的區塊執行後即跳出,不再檢查後續條件。因此條件順序至關重要(通常由嚴格到寬鬆)。
// 巢狀 if
if (user.isLoggedIn()) {
if (user.hasPermission("ADMIN")) {
showAdminPanel();
} else {
showUserPanel();
}
} else {
showLoginPage();
}
// 扁平化:使用邏輯運算子
if (user.isLoggedIn() && user.hasPermission("ADMIN")) {
showAdminPanel();
} else if (user.isLoggedIn()) {
showUserPanel();
} else {
showLoginPage();
}
// 錯誤!if 後加分號,導致區塊脫離條件控制
if (score >= 60); { // ← 這個分號等同於空敘述
System.out.println("及格"); // 無條件執行!
}
// 正確
if (score >= 60) {
System.out.println("及格");
}
int score = 75;
String result = (score >= 60) ? "及格" : "不及格";
// 等同於:
// String result;
// if (score >= 60) result = "及格"; else result = "不及格";
System.out.println(result); // 輸出: 及格
// 巢狀三元 (不建議,降低可讀性)
String grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "F";
// 經典累加:1 + 2 + ... + 100
int sum = 0;
int i = 1;
while (i <= 100) {
sum += i;
i++;
}
System.out.println("總和: " + sum); // 5050
// 讀取輸入直到 sentinel 值
Scanner sc = new Scanner(System.in);
int num;
while ((num = sc.nextInt()) != -1) { // -1 為結束標記
process(num);
}
// 選單驅動程式:至少顯示一次選單
int choice;
do {
showMenu();
choice = sc.nextInt();
handleChoice(choice);
} while (choice != 0); // 0 為離開
// 標準形式
for (int i = 1; i <= 10; i++) {
System.out.print(i + " "); // 1 2 3 ... 10
}
// 多重初始化/更新
for (int i = 0, j = 10; i < j; i++, j--) {
System.out.println("i=" + i + ", j=" + j);
}
// 增強型 for (for-each):遍歷陣列/集合 (Java 5+)
int[] numbers = {3, 1, 4, 1, 5, 9};
for (int n : numbers) {
System.out.print(n + " ");
}
// 字串遍歷
for (char c : "Java".toCharArray()) {
System.out.print(c + "-"); // J-a-v-a-
}
| 迴圈類型 | 適用場景 | 關鍵特性 |
|---|---|---|
for | 已知迴圈次數、陣列索引存取、計數器邏輯 | 初始化、條件、更新集中管理 |
while | 未知次數、依條件持續、串流讀取 | 先判斷,可能零次執行 |
do-while | 選單、輸入驗證、至少執行一次 | 後判斷,保證最少一次 |
for-each | 遍歷集合/陣列、不需索引 | 語法簡潔、避免索引越界、不可修改集合結構 |
// break:立即跳出迴圈
for (int i = 1; i <= 100; i++) {
if (i == 50) break;
System.out.print(i + " "); // 只輸出 1~49
}
// continue:跳過本次迭代,直接進入下一次判斷
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) continue; // 跳過偶數
System.out.print(i + " "); // 1 3 5 7 9
}
// 標籤 break/continue:跳出多層巢狀迴圈
outer: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) break outer; // 直接跳出外層
System.out.println("i=" + i + ", j=" + j);
}
}
// 故意無窮迴圈 (需內部 break)
while (true) {
String input = readCommand();
if ("exit".equals(input)) break;
process(input);
}
// 防禦性:避免死循環
int maxIterations = 10000;
int count = 0;
while (condition && count++ < maxIterations) {
// 處理邏輯
}
if (count >= maxIterations) log.warn("迴圈超過最大次數,可能發生死循環");
int day = 3;
String dayName;
switch (day) {
case 1: dayName = "星期一"; break;
case 2: dayName = "星期二"; break;
case 3: dayName = "星期三"; break;
case 4: dayName = "星期四"; break;
case 5: dayName = "星期五"; break;
case 6: dayName = "星期六"; break;
case 7: dayName = "星期日"; break;
default: dayName = "無效日期"; break;
}
System.out.println(dayName); // 星期三
關鍵規則:
int、char、String (Java 7+)、enum (Java 5+)break 會繼續執行下一個 case(有時可利用此特性合併 case)default 為可選,建議保留處理預期外數值
// 箭頭語法:無 fall-through、可作為表達式回傳值
String dayName = switch (day) {
case 1 -> "星期一";
case 2 -> "星期二";
case 3 -> "星期三";
case 4 -> "星期四";
case 5 -> "星期五";
case 6, 7 -> "週末"; // 多值合併
default -> "無效日期";
};
// 區塊語法:複雜邏輯、需用 yield 回傳
int dayType = switch (day) {
case 1, 2, 3, 4, 5 -> {
System.out.println("平日");
yield 1; // 工作日
}
case 6, 7 -> {
System.out.println("假日");
yield 2; // 休息日
}
default -> {
System.out.println("錯誤");
yield -1;
}
};
// 型別模式匹配
Object obj = "Hello";
String desc = switch (obj) {
case Integer i -> "整數: " + i;
case String s -> "字串長度: " + s.length();
case Double d -> "浮點數: " + d;
case null -> "空值";
default -> "其他型別: " + obj.getClass().getSimpleName();
};
// 守衛條件 (when)
int num = 15;
String category = switch (num) {
case int n when n < 0 -> "負數";
case int n when n == 0 -> "零";
case int n when n % 2 == 0 -> "正偶數";
case int n -> "正奇數"; // 預設匹配剩餘 int
default -> "非整數";
};
enum OrderStatus { PENDING, PAID, SHIPPED, DELIVERED, CANCELLED }
OrderStatus status = OrderStatus.PAID;
String message = switch (status) {
case PENDING -> "訂單待付款";
case PAID -> "付款成功,備貨中";
case SHIPPED -> "已出貨,追蹤號: " + generateTrackingNo();
case DELIVERED -> "已送達,請確認收貨";
case CANCELLED -> "訂單已取消";
// 無需 default,編譯器會檢查是否涵蓋所有 enum 值
};
// 傳統寫法:需手動 close,易漏寫
FileReader fr = null;
try {
fr = new FileReader("data.txt");
// 讀取...
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) try { fr.close(); } catch (IOException ignore) {}
}
// 現代寫法:自動關閉,實作 AutoCloseable 介面即可
try (FileReader fr = new FileReader("data.txt");
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
public void setName(String name) {
this.name = Objects.requireNonNull(name, "名稱不可為空");
// 等同於:
// if (name == null) throw new NullPointerException("名稱不可為空");
}
public void setAge(int age) {
if (age < 0 || age > 150)
throw new IllegalArgumentException("年齡必須在 0-150 之間: " + age);
this.age = age;
}
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 篩選、轉換、收集
List evenSquares = numbers.stream()
.filter(n -> n % 2 == 0) // 篩選偶數
.map(n -> n * n) // 平方
.collect(Collectors.toList()); // [4, 16, 36, 64, 100]
// 短路操作:找第一個符合條件
Optional firstBig = numbers.stream()
.filter(n -> n > 5)
.findFirst(); // Optional[6]
// 條件判斷
boolean anyMatch = numbers.stream().anyMatch(n -> n > 100); // false
boolean allMatch = numbers.stream().allMatch(n -> n > 0); // true
public boolean isPrime(int n) {
if (n <= 1) return false;
if (n <= 3) return true; // 2, 3 是質數
if (n % 2 == 0 || n % 3 == 0) return false; // 排除 2, 3 的倍數
// 只需檢查到 sqrt(n),且跳過 6 的倍數附近
for (int i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
// 測試
for (int i = 1; i <= 50; i++) {
if (isPrime(i)) System.out.print(i + " "); // 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
}
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) { // 下三角
System.out.printf("%dx%d=%-2d ", j, i, i*j);
}
System.out.println();
}
/*
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
...
*/
import java.util.*;
public class GuessNumberGame {
public static void main(String[] args) {
Random rand = new Random();
int answer = rand.nextInt(100) + 1; // 1-100
Scanner sc = new Scanner(System.in);
int guess, count = 0;
System.out.println("=== 猜數字遊戲 (1-100) ===");
do {
System.out.print("請輸入猜測: ");
guess = sc.nextInt();
count++;
if (guess > answer) System.out.println("太大了!");
else if (guess < answer) System.out.println("太小了!");
else System.out.printf("恭喜!答案是 %d,共猜了 %d 次%n", answer, count);
} while (guess != answer);
}
}
// 迴圈版:O(n) 時間、O(1) 空間
public long fibIterative(int n) {
if (n <= 1) return n;
long a = 0, b = 1;
for (int i = 2; i <= n; i++) {
long c = a + b;
a = b;
b = c;
}
return b;
}
// 遞迴版:O(2^n) 指數時間、O(n) 堆疊空間 (教學用,實務避免)
public long fibRecursive(int n) {
if (n <= 1) return n;
return fibRecursive(n-1) + fibRecursive(n-2);
}
// 記憶化遞迴:O(n) 時間
Map memo = new HashMap<>();
public long fibMemo(int n) {
if (n <= 1) return n;
return memo.computeIfAbsent(n, k -> fibMemo(k-1) + fibMemo(k-2));
}
| 錯誤類型 | 錯誤程式碼 | 正確寫法 |
|---|---|---|
| 浮點數比較 | if (x == 0.1 + 0.2) | if (Math.abs(x - 0.3) < 1e-9) |
| 字串比較 | if (str == "hello") | if ("hello".equals(str)) |
| 迴圈邊界 | for (int i=0; i<=arr.length; i++) | for (int i=0; i |
| 修改集合 | for (String s: list) if (cond) list.remove(s); | list.removeIf(cond); 或 Iterator |
| switch 漏 break | case 1: ... case 2: ... | 加上 break 或用 switch expression |
isUserValid 勝過 check(),MAX_RETRY 勝過 3new String())StringBuildernew ArrayList<>(expectedSize)int 而非包裝型別 Integer流程控制是程式設計的骨架。熟練掌握 if-else、switch、for/while/do-while、break/continue、try-with-resources 等語句,並理解現代 Java (switch expression、pattern matching、Stream API) 的演進,能讓你寫出更清晰、健壯、高效的程式碼。建議配合 LeetCode、HackerRank 等平台大量練習,將語法內化為直覺邏輯。