Skip to content

บทที่ 3 — Control Flow (if, switch, loop)

← บทที่ 2 | สารบัญ | บทที่ 4: Methods →

โปรแกรมจริง ๆ ไม่ได้แค่ "run บรรทัด 1, 2, 3, จบ" — มันต้อง ตัดสินใจ (ถ้า... ทำสิ่งนี้, ไม่ใช่... ทำอีกสิ่ง) และ ทำซ้ำ (ทำ 10 ครั้ง, ทำจนกว่า user จะกด exit)

นี่คือ control flow — การควบคุมว่า "บรรทัดไหนจะ run, บรรทัดไหนจะไม่ run, บรรทัดไหนจะ run กี่ครั้ง"

หลังจบบทนี้ คุณจะ:

  • ใช้ if, else if, else ตัดสินใจได้
  • ใช้ switch (ทั้งแบบเก่าและ Java 21+ style)
  • เขียน loop ด้วย for, while, do-while, for-each
  • ใช้ break กับ continue ควบคุม loop
  • เข้าใจ "ทำไม nested condition ลึก ๆ ไม่ดี" + วิธีลด nesting

1. if-else

1.1 แบบง่ายสุด

java
int age = 20;

if (age >= 18) {
    System.out.println("Adult");
}

อ่านได้ว่า: "ถ้า age >= 18 เป็นจริง ให้ทำใน block {...}"

ในวงเล็บต้องเป็น boolean (true/false) — Java ห้ามใช้ int (ต่างจาก C)

1.2 if-else

java
if (age >= 18) {
    System.out.println("Adult");
} else {
    System.out.println("Minor");
}

"ถ้าจริงทำ A, ไม่จริงทำ B"

1.3 if-else if-else (multi-way)

java
int score = 75;

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

Java จะ เช็คจากบนลงล่าง หยุดที่อันแรกที่เป็น true

1.4 ⚠️ Pitfall — ลืม { }

java
// ผิด แต่ compile ผ่าน!
if (age >= 18)
    System.out.println("Adult");
    System.out.println("Welcome!");   // ❌ ไม่อยู่ใน if! print เสมอ

ถ้าไม่ใส่ { } Java จะถือว่า if มีแค่ statement ถัดไปบรรทัดเดียว

กฎ rule of thumb: ใส่ { } เสมอ แม้มี statement เดียว — กัน bug + อ่านง่าย

1.5 ⚠️ Pitfall — == กับ =

java
if (age = 18) { ... }   // ❌ error: ไม่ใช่ boolean (= คือ assignment)
if (age == 18) { ... }  // ✅

Java เซฟกว่า C ตรงนี้ — compile error ทันที (C compile ผ่าน, run แล้วผิด)

1.6 Nested if

java
if (isLoggedIn) {
    if (isAdmin) {
        if (hasPermission) {
            // ทำอะไร...
        }
    }
}

อ่านยาก → ใช้ early return หรือ && แทน:

java
// แบบ &&
if (isLoggedIn && isAdmin && hasPermission) {
    // ทำอะไร...
}

// แบบ early return (ในmethod)
// (การประกาศ method เรียนบทที่ 4 — ตอนนี้ดูแค่ไอเดีย "return เร็ว")
public void doSomething() {
    if (!isLoggedIn) return;
    if (!isAdmin) return;
    if (!hasPermission) return;
    
    // ทำอะไร... (main logic)
}

💡 Best practice: ลด nesting โดยเช็ค "เคสที่ไม่ทำ" ก่อนแล้ว return — ลด indent ได้เยอะ อ่านง่ายขึ้น


2. switch

2.1 switch แบบเก่า (classic)

java
int day = 3;

switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    default:
        System.out.println("Unknown");
}

วิเคราะห์:

  • switch (day) — เช็คค่าของ day
  • case 1: — ถ้า day == 1
  • break;สำคัญมาก — ออกจาก switch ถ้าไม่ใส่จะ "fall through" ลงไปทำ case ถัดไปด้วย
  • default: — เคสที่ไม่ match อะไรเลย (เหมือน else)

2.2 ⚠️ Pitfall — ลืม break (fall-through)

java
switch (day) {
    case 1:
        System.out.println("Monday");
        // ลืม break!
    case 2:
        System.out.println("Tuesday");
        break;
}
// day = 1 → print "Monday" + "Tuesday" 😱

นี่คือเหตุผลที่ switch แบบเก่ามีปัญหา — ลืม break ได้ง่ายมาก ภาษา Java ใหม่จึงมีแบบที่ดีกว่า ดู section 2.3

2.3 switch แบบใหม่ (Java 14+) — Switch Expression

java
String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4, 5 -> "Late week";        // หลายค่าได้
    default -> "Unknown";
};

ข้อดี:

  • ใช้ -> แทน : + ไม่ต้อง break
  • เป็น expression — return ค่ากลับเป็นตัวแปรได้
  • บังคับ exhaustive (ครอบคลุมทุกกรณี) — switch expression ต้องครอบคลุมทุกเคสหรือมี default ไม่งั้น compile error (switch statement แบบเก่าที่ใช้ : + break ไม่บังคับ)
  • หลายค่าใน case เดียว: case 4, 5 ->
ขั้นสูง — enum และ sealed (ข้ามได้รอบแรก)

enum และ sealed type คือ class แบบพิเศษที่กำหนดค่าได้ครบทุกกรณีตายตัว — ถ้าใช้กับ switch expression และ cover ครบทุกค่า compiler จะไม่บังคับให้มี default (เดี๋ยวบทที่ 10 จะอธิบายละเอียด)

2.4 Switch กับ block (มีหลาย statement)

java
String result = switch (day) {
    case 1 -> "Monday";
    case 2 -> {
        System.out.println("Tuesday processing...");
        yield "Tuesday";   // ใช้ yield แทน return
    }
    default -> "Unknown";
};

yield = "return จาก block ของ switch expression"

2.4.5 case null + case null, default (Java 21+)

ก่อน Java 21 — switch ที่รับ Object แล้ว object เป็น null → throw NullPointerException ทันที (ก่อนเข้าทุก case)

Java 21+: รับ null ได้:

java
Object obj = null;

String result = switch (obj) {
    case null -> "null";
    case String s -> "string: " + s;
    case Integer i -> "int: " + i;
    default -> "other";
};

หรือรวม null กับ default:

java
String result = switch (obj) {
    case String s -> "string: " + s;
    case Integer i -> "int: " + i;
    case null, default -> "null หรืออื่น ๆ";
};

⚠️ ถ้า switch มี case ใด ๆ ที่เป็น Object pattern (String s, Integer i, ฯลฯ) แล้วไม่มี case null หรือ case null, default — null ยัง throw NPE เหมือนเดิม (backward compatible)

2.5 Pattern matching switch (Java 21+ stable)

แอบดูล่วงหน้า — เปิดดูหลังเรียนบทที่ 6 (object/polymorphism)

Java 21+ มีของใหม่ชื่อ pattern matching switch ที่เช็ค type ของ object แล้วทำงานตามชนิดที่เจอได้ — แต่ใช้คอนเซปต์ object/cast/polymorphism ที่ยังไม่ได้เรียน

ตอนนี้รู้แค่ว่า มี feature นี้อยู่ ก็พอ — เดี๋ยวบทที่ 10 จะเล่าละเอียดพร้อมตัวอย่างที่อ่านเข้าใจง่าย

2.6 เมื่อไหร่ใช้ if vs switch

สถานการณ์ใช้อะไร
2-3 conditionif-else
4+ condition แบบ "ค่านี้ตรงกับอะไร"switch
Range เช่น if (x > 100)if-else
ผสมหลาย fieldif-else

3. Loop

3.1 while

ทำซ้ำตราบใดที่ condition เป็น true:

java
int count = 0;
while (count < 5) {
    System.out.println("Count: " + count);
    count++;
}
// print: 0, 1, 2, 3, 4

⚠️ Infinite loop — ถ้าลืมเพิ่ม count:

java
int count = 0;
while (count < 5) {
    System.out.println("Count: " + count);
    // ลืม count++!
}
// loop ไม่จบ — กด Ctrl+C ใน terminal เพื่อหยุด (ถ้า run ใน IntelliJ ให้กดปุ่ม Stop ■ สีแดงที่ panel Run)

3.2 do-while

เหมือน while แต่ ทำอย่างน้อย 1 ครั้ง เสมอ:

java
int x = 100;
do {
    System.out.println("Running");
    x++;
} while (x < 5);
// print "Running" 1 ครั้ง (เพราะ x = 100 ตั้งแต่ต้น แต่ทำก่อนเช็ค)

ต่างกับ while:

java
int y = 100;
while (y < 5) {
    System.out.println("Running");
    y++;
}
// ไม่ print เลย (condition false ตั้งแต่แรก)

ใช้น้อย — เกือบทุกกรณีใช้ while ก็ได้

3.3 for loop — แบบ classic

java
for (int i = 0; i < 5; i++) {
    System.out.println("i = " + i);
}
// print: 0, 1, 2, 3, 4

วิเคราะห์ 3 ส่วน:

  1. Init: int i = 0 — run ครั้งเดียว ตอนเริ่ม
  2. Condition: i < 5 — เช็คทุกรอบก่อนทำ
  3. Update: i++ — run หลังจบแต่ละรอบ

ลำดับ execution:

text
init → condition (true?) → body → update → condition → body → update → ... → condition (false) → exit

เปรียบเทียบกับ while

java
// for
for (int i = 0; i < 5; i++) {
    body();
}

// while ที่เทียบเท่า
int i = 0;
while (i < 5) {
    body();
    i++;
}

For จะ compact กว่า — เหมาะเมื่อรู้จำนวนรอบล่วงหน้า

3.4 ตัวอย่าง for ที่ใช้บ่อย

java
// นับถอยหลัง
for (int i = 10; i > 0; i--) { ... }

// ทีละ 2
for (int i = 0; i < 100; i += 2) { ... }

// loop ทุก index ของ array
int[] arr = {10, 20, 30};
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

3.5 for-each loop

ถ้าไม่ต้องการ index — แค่อยาก iterate ทุก element:

java
int[] arr = {10, 20, 30};
for (int n : arr) {
    System.out.println(n);
}

อ่านได้ว่า: "สำหรับแต่ละ n ใน arr"

ใช้กับ array, List, Set, Map ได้

java
// บรรทัดนี้วางไว้ต้นไฟล์ ก่อน public class Main { ... }
import java.util.List;
java
// List = รายการข้อมูล — เรียนละเอียดบทที่ 7 ตอนนี้ดูแค่รูปแบบ for-each
// (IDE เช่น IntelliJ จะ auto-import ให้อัตโนมัติ — กด Alt+Enter บน List ที่ขีดแดง)
List<String> names = List.of("Anna", "Ben", "Cal");
for (String name : names) {
    System.out.println(name);
}

⚠️ ข้อจำกัด: for-each ไม่รู้ index → ถ้าต้องใช้ index ใช้ classic for

⚠️ ไม่ modify collection ระหว่าง for-each — อย่าเพิ่ม/ลบ item ใน list ขณะที่ for-each กำลังวนอยู่ ถ้าทำจะเกิด error พิเศษชื่อ ConcurrentModificationException (exception = error ตอน run — เดี๋ยวบทที่ 8 จะอธิบาย, ตอนนี้รู้แค่ว่าอย่าทำแบบนี้ก็พอ) — กฎเดียวกันนี้ใช้กับ Map.keySet()/entrySet() ตอน iterate ด้วย (เดี๋ยวบทที่ 7 จะเจอ)


4. break และ continue

4.1 break — ออกจาก loop ทันที

java
for (int i = 0; i < 10; i++) {
    if (i == 5) {
        break;   // ออกทันที
    }
    System.out.println(i);
}
// print: 0, 1, 2, 3, 4

4.2 continue — ข้ามรอบนี้ ไปรอบถัดไป

java
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
        continue;   // ข้ามถ้าคู่
    }
    System.out.println(i);
}
// print: 1, 3, 5, 7, 9

4.3 Labeled break (loop ซ้อน)

label คือชื่อที่เราตั้งให้ loop เพื่อระบุว่าอยาก break หรือ continue loop ไหน — เขียนด้วยชื่อตามด้วย : วางไว้ก่อน for

java
outer:
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) {
            break outer;   // break ออกจาก loop ภายนอกเลย
        }
        System.out.println(i + "," + j);
    }
}

ใช้น้อย — ถ้าซ้อนลึกแบบนี้ลอง refactor เป็น method ก่อน


5. Pattern ที่ใช้บ่อย

5.1 Sum หา min/max

java
int[] numbers = {3, 1, 4, 1, 5, 9, 2, 6};

int sum = 0;
for (int n : numbers) {
    sum += n;
}
System.out.println("Sum: " + sum);

int max = numbers[0];
for (int n : numbers) {
    if (n > max) max = n;
}
System.out.println("Max: " + max);

5.2 Count

java
int count = 0;
for (int n : numbers) {
    if (n > 3) count++;
}
System.out.println("Numbers > 3: " + count);
java
int target = 5;
int foundIndex = -1;
for (int i = 0; i < numbers.length; i++) {
    if (numbers[i] == target) {
        foundIndex = i;
        break;
    }
}
if (foundIndex >= 0) {
    System.out.println("Found at index " + foundIndex);
} else {
    System.out.println("Not found");
}

5.4 Building string

java
// (fragment — ต้องอยู่ใน class และ method ก่อนนำไปรัน)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
    sb.append("Item ").append(i).append(", ");
}
String result = sb.toString();

⚠️ อย่าใช้ + ใน loop — ช้ามาก เพราะ String immutable ทุกครั้งสร้างใหม่ ใช้ StringBuilder แทน


6. Checkpoint

🛠️ Checkpoint 3.1 — FizzBuzz

โจทย์คลาสสิก: print 1-100, ถ้าหารด้วย 3 ลงตัวพิมพ์ "Fizz", หารด้วย 5 ลงตัวพิมพ์ "Buzz", หารทั้งคู่ลงตัวพิมพ์ "FizzBuzz", ไม่งั้นพิมพ์เลข

📖 เฉลย
java
public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 100; i++) {
            if (i % 15 == 0) {              // 3 และ 5 ลงตัว = 15 ลงตัว
                System.out.println("FizzBuzz");
            } else if (i % 3 == 0) {
                System.out.println("Fizz");
            } else if (i % 5 == 0) {
                System.out.println("Buzz");
            } else {
                System.out.println(i);
            }
        }
    }
}

สังเกต: เช็ค % 15 ก่อน เพราะถ้าเช็ค % 3 ก่อน เลข 15 จะตก case "Fizz" ก่อน

🛠️ Checkpoint 3.2 — Sum 1 ถึง N

รับ N จาก args[0] แล้วหา sum 1+2+...+N ทดสอบ: java Main 1005050

📖 เฉลย
java
public class Main {
    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        System.out.println(sum);
    }
}

🛠️ Checkpoint 3.3 — เช็ค prime

รับ N จาก args[0] ตรวจว่า prime หรือไม่ ใบ้: ถ้า N หารด้วยตัวเลข 2 ถึง N-1 ไม่ลงตัวเลย = prime ปรับให้เร็วขึ้น: เช็คแค่ถึง Math.sqrt(N) พอ (Math.sqrt(n) = หารากที่สอง — class Math มากับ Java เรียกได้เลยไม่ต้อง import)

📖 เฉลย
java
public class Main {
    public static void main(String[] args) {
        int n = Integer.parseInt(args[0]);
        
        if (n < 2) {
            System.out.println(n + " is not prime");
            return;
        }
        
        boolean isPrime = true;
        for (int i = 2; i * i <= n; i++) {   // i*i <= n เทียบเท่า i <= sqrt(n) และเร็วกว่า (ไม่ต้อง call sqrt ทุกรอบ)
            if (n % i == 0) {
                isPrime = false;
                break;
            }
        }
        
        System.out.println(n + " is " + (isPrime ? "prime" : "not prime"));
    }
}

🛠️ Checkpoint 3.4 — เกรดจาก switch

ใช้ switch expression (สไตล์ใหม่) แปลงคะแนนเป็นเกรด:

  • 90+: A
  • 80-89: B
  • 70-79: C
  • 60-69: D
  • ต่ำกว่า: F

ใบ้: หาร score ด้วย 10 แล้ว switch เลขผลลัพธ์ (10/9 → A, 8 → B, ...) — หรือใช้ if-else ก็ได้

📖 เฉลย
java
public class Main {
    public static void main(String[] args) {
        int score = Integer.parseInt(args[0]);
        
        String grade = switch (score / 10) {
            case 10, 9 -> "A";
            case 8 -> "B";
            case 7 -> "C";
            case 6 -> "D";
            default -> "F";
        };
        
        System.out.println("Grade: " + grade);
    }
}

เทคนิค: หารด้วย 10 → ทำให้ 95 กลายเป็น 9, 85 กลายเป็น 8 — ใช้ switch ได้สะดวก


7. สรุปบท

if-else ตัดสินใจตาม condition (boolean) ✅ switch เหมาะกับ "เทียบค่าตรง ๆ" หลาย ๆ ค่า — Java 14+ มี switch expression ✅ while / do-while / for / for-each — เลือกตามสถานการณ์ ✅ break ออกจาก loop, continue ข้ามรอบนี้ ✅ ลด nesting ด้วย early return + && ✅ ใช้ StringBuilder แทน + ใน loop

→ ไปบทที่ 4: Methods + Scope