Chuyển tới nội dung chính

Luồng điều khiển (Control Flow)

Mục tiêu bài học

Sau bài này, bạn sẽ:

  • Áp dụng các cấu trúc điều khiển if/else, switch (traditional và enhanced) để ra quyết định
  • Sử dụng các vòng lặp for, for-each, while, do-while để xử lý dữ liệu lặp đi lặp lại
  • Kiểm soát luồng chương trình với break, continue và labeled statements
  • Hiểu unreachable code detection của Java compiler

Bài trước: Toán tử (Operators) — Đã học về các loại operators và operator precedence. Bài này sẽ học cách điều khiển luồng chương trình.

If/Else

1. if Statement

if (condition) {
// Execute nếu condition = true
}

Ví dụ:

int age = 20;

if (age >= 18) {
System.out.println("Bạn đã trưởng thành");
}

2. if-else Statement

if (condition) {
// Execute nếu true
} else {
// Execute nếu false
}

Ví dụ:

int score = 75;

if (score >= 50) {
System.out.println("Đậu");
} else {
System.out.println("Rớt");
}

3. if-else if-else Statement

if (condition1) {
// Execute nếu condition1 = true
} else if (condition2) {
// Execute nếu condition2 = true
} else {
// Execute nếu tất cả đều false
}

Ví dụ:

int score = 85;

if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B");
} else if (score >= 70) {
System.out.println("Grade: C");
} else if (score >= 60) {
System.out.println("Grade: D");
} else {
System.out.println("Grade: F");
}

4. Nested if

int age = 25;
boolean hasLicense = true;

if (age >= 18) {
if (hasLicense) {
System.out.println("Có thể lái xe");
} else {
System.out.println("Cần bằng lái");
}
} else {
System.out.println("Chưa đủ tuổi");
}
Best Practice
  • Dùng {} braces ngay cả khi chỉ có 1 statement
    // ❌ BAD - Error-prone
    if (condition)
    statement1;
    statement2; // Không thuộc if! (indentation misleading)

    // ✅ GOOD - Clear
    if (condition) {
    statement1;
    statement2;
    }

Switch Statement

Traditional Switch (Java 1-13)

switch (expression) {
case value1:
// Statements
break;
case value2:
// Statements
break;
default:
// Default statements
}

Ví dụ:

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

Fall-through behavior:

int month = 2;

switch (month) {
case 12:
case 1:
case 2:
System.out.println("Winter");
break; // Quan trọng! Không có break → fall through
case 3:
case 4:
case 5:
System.out.println("Spring");
break;
default:
System.out.println("Other season");
}
break là bắt buộc!

Nếu quên break, code sẽ "fall through" sang cases tiếp theo!

int x = 1;
switch (x) {
case 1:
System.out.println("One");
// ❌ Forgot break!
case 2:
System.out.println("Two");
break;
}
// Output:
// One
// Two (fall-through!)

Enhanced Switch (Java 14+)

Switch expressions - modern, concise syntax.

int day = 3;

String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
case 6 -> "Saturday";
case 7 -> "Sunday";
default -> "Invalid day";
};

System.out.println(dayName); // "Wednesday"

Benefits:

  • ✅ No break needed
  • ✅ Can return values directly
  • ✅ More concise
  • ✅ No fall-through (safer)

Multiple values:

String season = switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
case 6, 7, 8 -> "Summer";
case 9, 10, 11 -> "Fall";
default -> "Invalid month";
};

Block with yield:

int score = 85;

String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> "C";
case 6 -> {
System.out.println("Barely passed");
yield "D"; // yield để return value từ block
}
default -> "F";
};

Switch Statement OCP Traps

🔥 Bẫy OCP: Switch common errors

Trap 1: Fall-through behavior (traditional switch)

int day = 1;
String result = "";

switch (day) {
case 1:
result = "Monday";
// ❌ Forgot break!
case 2:
result = "Tuesday";
break;
}
System.out.println(result); // "Tuesday" (not "Monday"!)

Trap 2: Switch type restrictions

Switch expression chỉ accept:

  • Primitive: byte, short, char, int (KHÔNG có long, float, double)
  • Wrapper: Byte, Short, Character, Integer
  • String (Java 7+)
  • enum types
  • var (nếu type hợp lệ)
// ✅ Valid
int x = 5;
switch (x) { }

String s = "Hello";
switch (s) { }

// ❌ Invalid types
long l = 100L;
switch (l) { } // Compile error! long not allowed

double d = 3.14;
switch (d) { } // Compile error! double not allowed

boolean b = true;
switch (b) { } // Compile error! boolean not allowed

Trap 3: Case values must be constants

final int OPTION_A = 1;
int optionB = 2; // Not final!

int choice = 1;
switch (choice) {
case OPTION_A: // ✅ OK - final constant
break;
case optionB: // ❌ Compile error! Not a constant expression
break;
}

Trap 4: Duplicate case labels

int x = 5;
switch (x) {
case 1:
System.out.println("One");
break;
case 1: // ❌ Compile error! Duplicate case
System.out.println("Also one");
break;
}

Trap 5: Default placement (traditional switch)

// ✅ Default có thể ở bất kỳ đâu (nhưng nên ở cuối)
int x = 5;
switch (x) {
default: // ✅ OK - default đầu tiên
System.out.println("Default");
break;
case 1:
System.out.println("One");
break;
}

// Nhưng vẫn fall-through!
switch (x) {
default:
System.out.println("Default");
// ❌ Forgot break → falls through to case 1!
case 1:
System.out.println("One");
break;
}
// Output: Default\nOne

Trap 6: Enhanced switch requires exhaustiveness (for expressions)

int day = 1;

// ❌ Compile error if not exhaustive
String dayName = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
// Missing other cases and no default!
};

// ✅ Must have default or cover all cases
String dayName2 = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
default -> "Other day";
};

Trap 7: yield vs return

String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> {
System.out.println("Good!");
yield "B"; // ✅ Use yield in switch expression
// return "B"; // ❌ return exits method, not switch!
}
default -> "C";
};

Trap 8: Mixing arrow and colon syntax

// ❌ Cannot mix syntaxes in same switch
switch (day) {
case 1 -> System.out.println("Monday"); // Arrow syntax
case 2: // ❌ Compile error! Cannot mix with colon syntax
System.out.println("Tuesday");
break;
}

Unreachable Code Detection

🔥 Bẫy OCP: Unreachable code

Java compiler detects unreachable statements và báo compile error.

Case 1: After return

public int test() {
return 10;
System.out.println("Hello"); // ❌ Compile error: unreachable statement
}

Case 2: After break in loop

for (int i = 0; i < 10; i++) {
break;
System.out.println(i); // ❌ Unreachable
}

Case 3: After continue

while (true) {
continue;
System.out.println("Hi"); // ❌ Unreachable
}

Case 4: Constant condition

while (false) {
System.out.println("Never runs"); // ❌ Unreachable
}

// But this is OK (compiler doesn't analyze variable values):
boolean flag = false;
while (flag) {
System.out.println("OK"); // ✅ Compiles (even though never runs)
}

// if với constant condition:
if (false) {
System.out.println("Never"); // ❌ Unreachable
}

Case 5: After throw

public void test() {
throw new RuntimeException();
System.out.println("Hi"); // ❌ Unreachable
}

Case 6: Unreachable catch block

try {
// ...
} catch (FileNotFoundException e) {
// Handle file not found
} catch (IOException e) {
// ❌ Unreachable if FileNotFoundException is subclass of IOException
// (must put more specific exception first)
}

Tricky case: Infinite loop không báo unreachable

while (true) {
System.out.println("Runs forever");
}
System.out.println("After loop"); // ❌ Unreachable (compile error!)

// Nhưng:
for (;;) {
System.out.println("Runs forever");
}
System.out.println("After loop"); // ❌ Unreachable (compile error!)

Loops (Vòng lặp)

1. for Loop

Syntax:

for (initialization; condition; update) {
// Loop body
}

Ví dụ:

// In số từ 1 đến 5
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
// Output: 1 2 3 4 5

Execution flow:

  1. Initialization: int i = 1 (chỉ chạy 1 lần)
  2. Condition: Check i <= 5
  3. Body: Execute loop body
  4. Update: i++
  5. Repeat từ bước 2

Common patterns:

// Đếm lên
for (int i = 0; i < 10; i++) {
System.out.println(i); // 0 to 9
}

// Đếm xuống
for (int i = 10; i > 0; i--) {
System.out.println(i); // 10 to 1
}

// Bước nhảy 2
for (int i = 0; i < 10; i += 2) {
System.out.println(i); // 0, 2, 4, 6, 8
}

// Multiple variables
for (int i = 0, j = 10; i < j; i++, j--) {
System.out.println(i + " " + j);
}

2. Enhanced for Loop (for-each)

Dùng để iterate qua arrays hoặc collections.

Syntax:

for (type variable : array/collection) {
// Loop body
}

Ví dụ:

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

for (int num : numbers) {
System.out.println(num);
}

String[] names = {"Alice", "Bob", "Charlie"};
for (String name : names) {
System.out.println(name);
}
for vs for-each

Dùng for-each khi:

  • ✅ Chỉ cần đọc giá trị (không modify)
  • ✅ Không cần index

Dùng for khi:

  • ✅ Cần index
  • ✅ Cần modify elements
  • ✅ Cần iterate theo pattern khác (backward, skip, ...)

3. while Loop

Syntax:

while (condition) {
// Loop body
}

Ví dụ:

int i = 1;
while (i <= 5) {
System.out.println(i);
i++;
}
// Output: 1 2 3 4 5

Use cases:

// Read input until valid
Scanner sc = new Scanner(System.in);
int age = -1;

while (age < 0 || age > 120) {
System.out.print("Nhập tuổi (0-120): ");
age = sc.nextInt();
}

// Process unknown number of items
while (sc.hasNext()) {
String line = sc.nextLine();
// Process line
}

4. do-while Loop

Execute body ít nhất 1 lần, sau đó check condition.

Syntax:

do {
// Loop body (execute at least once)
} while (condition);

Ví dụ:

int i = 1;
do {
System.out.println(i);
i++;
} while (i <= 5);
// Output: 1 2 3 4 5

while vs do-while:

// while: Check trước, có thể không chạy body
int x = 10;
while (x < 5) {
System.out.println(x); // Không chạy (x = 10)
}

// do-while: Chạy body trước, check sau
do {
System.out.println(x); // Chạy 1 lần (in 10)
} while (x < 5);

Use case: Menu:

Scanner sc = new Scanner(System.in);
int choice;

do {
System.out.println("1. Option A");
System.out.println("2. Option B");
System.out.println("0. Exit");
System.out.print("Choice: ");
choice = sc.nextInt();

switch (choice) {
case 1 -> System.out.println("Selected A");
case 2 -> System.out.println("Selected B");
case 0 -> System.out.println("Goodbye!");
default -> System.out.println("Invalid!");
}
} while (choice != 0);

break và continue

break - Thoát khỏi loop

// Tìm số đầu tiên > 50
for (int i = 1; i <= 100; i++) {
if (i > 50) {
System.out.println("Found: " + i);
break; // Thoát loop
}
}
// Output: Found: 51

break trong nested loops:

// Chỉ thoát loop trong cùng
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) {
break; // Chỉ thoát inner loop
}
System.out.println(i + "," + j);
}
}
// Output: 1,1 2,1 3,1

continue - Bỏ qua iteration hiện tại

// In số lẻ
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
System.out.println(i);
}
// Output: 1 3 5 7 9

Labeled loops (Ít dùng)

Thoát khỏi nested loops.

outer:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (i == 2 && j == 2) {
break outer; // Thoát cả 2 loops
}
System.out.println(i + "," + j);
}
}
// Output: 1,1 1,2 1,3 2,1

Bài tập thực hành

Bài 1: Calculator với Switch

Viết calculator với các phép toán: +, -, *, /, % dùng enhanced switch expression.

Expected output:

Nhập số thứ nhất: 10
Nhập phép toán (+, -, *, /, %): *
Nhập số thứ hai: 5
Kết quả: 10 * 5 = 50
Xem đáp án
import java.util.Scanner;

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

System.out.print("Nhập số thứ nhất: ");
double num1 = sc.nextDouble();

System.out.print("Nhập phép toán (+, -, *, /, %): ");
char operator = sc.next().charAt(0);

System.out.print("Nhập số thứ hai: ");
double num2 = sc.nextDouble();

double result = switch (operator) {
case '+' -> num1 + num2;
case '-' -> num1 - num2;
case '*' -> num1 * num2;
case '/' -> num1 / num2;
case '%' -> num1 % num2;
default -> {
System.out.println("Phép toán không hợp lệ!");
yield 0;
}
};

System.out.printf("Kết quả: %.2f %c %.2f = %.2f%n",
num1, operator, num2, result);

sc.close();
}
}

Bài 2: In bảng cửu chương

Viết chương trình in bảng cửu chương từ 1 đến 10.

Expected output:

1 x 1 = 1
1 x 2 = 2
...
10 x 10 = 100
Xem đáp án
public class MultiplicationTable {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
System.out.printf("%d x %d = %d%n", i, j, i * j);
}
System.out.println(); // Blank line
}
}
}

Bài 3: Tìm số nguyên tố

Viết chương trình kiểm tra một số có phải số nguyên tố không.

Expected output:

Nhập số: 17
17 là số nguyên tố

Nhập số: 20
20 không phải số nguyên tố
Xem đáp án
import java.util.Scanner;

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

System.out.print("Nhập số: ");
int num = sc.nextInt();

boolean isPrime = true;

if (num <= 1) {
isPrime = false;
} else {
for (int i = 2; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
isPrime = false;
break;
}
}
}

if (isPrime) {
System.out.println(num + " là số nguyên tố");
} else {
System.out.println(num + " không phải số nguyên tố");
}

sc.close();
}
}

Bài 4: Fibonacci Sequence

In ra n số đầu tiên của dãy Fibonacci.

Expected output:

Nhập n: 10
0 1 1 2 3 5 8 13 21 34
Xem đáp án
import java.util.Scanner;

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

System.out.print("Nhập n: ");
int n = sc.nextInt();

int a = 0, b = 1;

for (int i = 0; i < n; i++) {
System.out.print(a + " ");

int next = a + b;
a = b;
b = next;
}

sc.close();
}
}

Tổng kết

Trong bài này, bạn đã học:

Control Flow:

  • if/else if/else
  • switch (traditional + enhanced với arrow syntax)

Loops:

  • for loop
  • for-each loop
  • while loop
  • do-while loop

Loop control:

  • break (thoát loop)
  • continue (skip iteration)
  • labeled loops (thoát nested loops)
Bước tiếp theo

Bài tiếp theo chúng ta sẽ học về Packages và Import - tổ chức code trong Java!

👉 Tiếp theo: Packages và Import →

Đọc thêm