Bỏ qua điều hướng
EN
Tất cả bài viết

Java cơ bản #3: rẽ nhánh if/switch và vòng lặp for, while

Dạy chương trình ra quyết định và lặp lại: if/else if/else, switch expression hiện đại với ->, yield và pattern, for, while, do-while, break/continue có nhãn, vòng lặp lồng nhau — qua các bài toán thật: xếp loại điểm, menu, đoán số, in bảng cửu chương.

11 phút đọc

Ôn tập bài nàyThẻ ghi nhớ & câu hỏi ôn tập6 thẻ ghi nhớ · 6 câu trắc nghiệm
Mục lục

Tới giờ chương trình của ta chạy thẳng từ trên xuống dưới. Phần mềm thật cần hai khả năng nữa: ra quyết định ("nếu số dư không đủ thì từ chối giao dịch") và lặp lại ("gửi email cho từng khách hàng trong danh sách"). Đó là cấu trúc rẽ nhánh và vòng lặp.

Bài này dùng cả cú pháp truyền thống lẫn switch expression hiện đại (Java 14+) — thứ làm code Java ngắn và an toàn hơn hẳn.

if / else

XepLoai.java
void main() {
    double diem = 7.8;
 
    if (diem >= 8.5) {
        IO.println("Giỏi");
    } else if (diem >= 7.0) {
        IO.println("Khá");
    } else if (diem >= 5.0) {
        IO.println("Trung bình");
    } else {
        IO.println("Yếu");
    }
}

Các điều kiện được xét từ trên xuống, gặp điều kiện đúng đầu tiên thì chạy khối đó và bỏ qua phần còn lại. Vì vậy thứ tự quan trọng: đặt diem >= 5.0 lên đầu thì điểm 9 cũng bị xếp "Trung bình".

Lưu ý: luôn dùng dấu { } kể cả khi khối chỉ có một lệnh. Thiếu ngoặc, lệnh thứ hai mà bạn thêm vào sau này sẽ nằm ngoài if dù thụt lề trông như bên trong — nguồn gốc của nhiều lỗi khó tìm.

Điều kiện kết hợp

NamNhuan.java
void main() {
    int nam = 2028;
    boolean nhuan = (nam % 4 == 0 && nam % 100 != 0) || nam % 400 == 0;
    IO.println(nam + (nhuan ? " là năm nhuận" : " không phải năm nhuận"));
}

Bẫy: so sánh chuỗi bằng ==

SoSanhChuoi.java
void main() {
    String a = "java";
    String b = new String("java");
    IO.println(a == b);          // false — so sánh ĐỊA CHỈ hai đối tượng
    IO.println(a.equals(b));     // true  — so sánh NỘI DUNG
    IO.println("JAVA".equalsIgnoreCase(a));   // true
}

== với kiểu tham chiếu kiểm tra "có phải cùng một đối tượng không". Luôn so sánh chuỗi bằng .equals(). Bài 4 sẽ giải thích kỹ hơn.

switch

Switch expression (Java 14+) — nên dùng

NgayTrongTuan.java
void main() {
    int thu = 6;
    String ten = switch (thu) {
        case 2 -> "Thứ Hai";
        case 3 -> "Thứ Ba";
        case 4 -> "Thứ Tư";
        case 5 -> "Thứ Năm";
        case 6 -> "Thứ Sáu";
        case 7 -> "Thứ Bảy";
        case 8, 1 -> "Chủ nhật";       // nhiều giá trị một nhánh
        default -> "Không hợp lệ";
    };
    IO.println(ten);
 
    String loaiNgay = switch (thu) {
        case 7, 8, 1 -> "cuối tuần";
        default -> {
            int conLai = 7 - thu;
            yield "ngày làm việc, còn " + conLai + " ngày tới thứ Bảy";   // khối nhiều lệnh trả giá trị bằng yield
        }
    };
    IO.println(loaiNgay);
}

Ưu điểm so với switch kiểu cũ:

  • Trả về giá trị → gán thẳng vào biến.
  • Mũi tên -> không rơi xuống (fall-through) nhánh sau — không cần break.
  • Switch expression phải phủ hết mọi trường hợp (thường bằng default), trình biên dịch kiểm tra giúp.

switch chạy được với int, char, String, enum (bài 8), và từ Java 21 với pattern theo kiểu đối tượng (bài 7).

SwitchChuoi.java
void main() {
    String lenh = "luu";
    switch (lenh) {
        case "mo" -> IO.println("Mở file");
        case "luu" -> IO.println("Lưu file");
        case "thoat" -> IO.println("Tạm biệt");
        default -> IO.println("Lệnh không hiểu: " + lenh);
    }
}

Switch kiểu cũ — để đọc hiểu code có sẵn

SwitchCu.java
void main() {
    int thang = 2;
    int soNgay;
    switch (thang) {
        case 4:
        case 6:
        case 9:
        case 11:
            soNgay = 30;
            break;           // quên break → chạy tiếp xuống nhánh dưới
        case 2:
            soNgay = 28;
            break;
        default:
            soNgay = 31;
    }
    IO.println("Tháng " + thang + " có " + soNgay + " ngày");
}

Vòng lặp for

Dùng khi biết trước số lần lặp:

VongFor.java
void main() {
    // for (khởi tạo; điều kiện; bước nhảy)
    for (int i = 1; i <= 5; i++) {
        IO.println("Lần lặp thứ " + i);
    }
 
    int tong = 0;
    for (int i = 2; i <= 100; i += 2) {   // tổng các số chẵn từ 2 tới 100
        tong += i;
    }
    IO.println("Tổng số chẵn: " + tong);   // 2550
 
    for (int i = 10; i > 0; i -= 3) {      // đếm ngược
        IO.print(i + " ");
    }
    IO.println("");
}

Biến i khai báo trong for chỉ tồn tại bên trong vòng lặp.

Vòng lặp lồng nhau: bảng cửu chương

BangCuuChuong.java
void main() {
    for (int i = 1; i <= 9; i++) {
        StringBuilder dong = new StringBuilder();
        for (int j = 2; j <= 5; j++) {
            dong.append(String.format("%d x %d = %-4d", j, i, i * j));
        }
        IO.println(dong);
    }
}

String.format("%-4d", ...) căn trái số trong 4 ô để các cột thẳng hàng. (StringBuilder sẽ học ở bài 4.)

while và do-while

while dùng khi không biết trước số lần lặp, chỉ biết điều kiện dừng:

VongWhile.java
void main() {
    // Bao nhiêu năm để 100 triệu gửi lãi 6%/năm thành 200 triệu?
    double tien = 100_000_000;
    int nam = 0;
    while (tien < 200_000_000) {
        tien *= 1.06;
        nam++;
    }
    IO.println("Sau " + nam + " năm: " + String.format("%,.0f", tien) + " đồng");
 
    // Đếm số chữ số của một số
    int n = 2026, demChuSo = 0;
    while (n > 0) {
        n /= 10;
        demChuSo++;
    }
    IO.println("Số chữ số: " + demChuSo);
}

do-while chạy thân vòng lặp ít nhất một lần rồi mới kiểm tra — hợp với menu và kiểm tra dữ liệu nhập:

DoWhile.java
void main() {
    int so;
    do {
        so = Integer.parseInt(IO.readln("Nhập số dương: "));
        if (so <= 0) IO.println("Số không hợp lệ, nhập lại.");
    } while (so <= 0);
    IO.println("Bạn nhập " + so);
}

Lưu ý: vòng lặp vô hạn ngoài ý muốn là lỗi phổ biến — thường do quên cập nhật biến trong điều kiện (while (tien < 200_000_000) mà quên tien *= 1.06). Chương trình treo thì bấm Ctrl + C để dừng.

break và continue

  • break: thoát ngay khỏi vòng lặp.
  • continue: bỏ qua phần còn lại của lần lặp hiện tại, sang lần kế.
BreakContinue.java
void main() {
    // In số lẻ từ 1 tới 15, dừng khi gặp số chia hết cho 7
    for (int i = 1; i <= 15; i++) {
        if (i % 2 == 0) continue;
        if (i % 7 == 0) break;
        IO.print(i + " ");
    }
    IO.println("");   // 1 3 5
 
    // Nhãn: thoát khỏi cả hai vòng lặp lồng nhau
    timKiem:
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= 9; j++) {
            if (i * j == 42) {
                IO.println("Tìm thấy: " + i + " x " + j);
                break timKiem;
            }
        }
    }
}

Nhãn (timKiem:) hiếm khi cần; nếu vòng lặp lồng phức tạp tới mức cần nhãn, thường nên tách thành một phương thức và dùng return (bài 5).

Ví dụ tổng hợp: trò chơi đoán số

DoanSo.java
import java.util.Random;
 
void main() {
    int bi = new Random().nextInt(1, 101);    // số ngẫu nhiên 1–100
    int soLan = 0;
    while (true) {
        String nhap = IO.readln("Đoán số (1-100): ");
        if (nhap == null) break;               // hết dữ liệu nhập (Ctrl+D / Ctrl+Z)
        int doan = Integer.parseInt(nhap.trim());
        soLan++;
        if (doan < bi) {
            IO.println("Lớn hơn!");
        } else if (doan > bi) {
            IO.println("Nhỏ hơn!");
        } else {
            IO.println("Chính xác sau " + soLan + " lần đoán!");
            break;
        }
    }
}

Chiến thuật tối ưu là luôn đoán số ở giữa khoảng còn lại — tối đa 7 lần cho 100 số. Đó chính là ý tưởng của tìm kiếm nhị phân, thuật toán bạn sẽ gặp lại rất nhiều.

Chọn cấu trúc nào

Tình huốngDùng
Hai, ba khả năng, điều kiện là khoảng giá trịif / else if / else
Nhiều giá trị rời rạc của một biếnswitch expression
Biết trước số lần lặp, hoặc duyệt theo chỉ sốfor
Duyệt mọi phần tử của mảng/danh sáchfor-each (bài 4)
Lặp tới khi điều kiện thay đổiwhile
Phải chạy ít nhất một lần (menu, nhập liệu)do-while

Bài tập

  1. Nhập ba số, in số lớn nhất (không dùng Math.max).
  2. In các số từ 1 tới 100; số chia hết cho 3 in Fizz, cho 5 in Buzz, cho cả hai in FizzBuzz — bài phỏng vấn kinh điển.
  3. Nhập n, kiểm tra n có phải số nguyên tố (chỉ cần thử chia tới n\sqrt{n}).
  4. In tam giác sao cao n dòng bằng vòng lặp lồng nhau.
  5. Viết menu máy tính bỏ túi bằng do-while + switch: chọn phép toán, nhập hai số, in kết quả, chọn 0 để thoát.

Tổng kết

  • if / else if / else xét điều kiện từ trên xuống; luôn dùng { }.
  • So sánh chuỗi bằng .equals(), không bằng ==.
  • Ưu tiên switch expression với ->: trả giá trị, không rơi nhánh, nhiều giá trị một nhánh, yield cho khối nhiều lệnh.
  • for khi biết số lần lặp, while khi chỉ biết điều kiện dừng, do-while khi cần chạy ít nhất một lần.
  • break thoát vòng lặp, continue bỏ qua lần lặp hiện tại.
  • Bài tiếp theo: mảng và chuỗi — lưu và xử lý nhiều giá trị cùng lúc.

Nguồn tham khảo: dev.java — "Control Flow Statements", "Branching with Switch Expressions"; JEP 361: Switch Expressions; Java Programming MOOC — University of Helsinki, phần 1–2.

Đang tải câu hỏi luyện tập…

Bài này thế nào?

Bài viết có hữu ích với bạn?

Đánh giá giúp mình biết nên viết thêm về chủ đề nào.

Đang tải đánh giá…
Chọn số sao từ 1 đến 5

Bình luận

Đăng nhập để bình luận không cần nhập tên và được duyệt nhanh hơn.

0/2000
Đang tải bình luận…

Đọc tiếp

Bài số № 55

Java cơ bản #12: dự án cuối — ứng dụng quản lý chi tiêu với Maven và JUnit

Gom toàn bộ kiến thức vào một dự án thật: cấu trúc Maven và pom.xml, chia lớp model–kho dữ liệu–service–giao diện, lưu file CSV, thống kê bằng Stream, cảnh báo ngân sách, viết kiểm thử JUnit 5, đóng gói file jar chạy được — và lộ trình học tiếp sau Java cơ bản.

19 phút đọc

Bài số № 54

Java cơ bản #11: lambda, tham chiếu phương thức, Stream API và Optional

Lập trình kiểu hàm trong Java: lambda và functional interface (Predicate, Function, Consumer, Supplier), tham chiếu phương thức ::, Stream với filter–map–reduce, sorted, collect, groupingBy, Optional thay cho null — cùng lời khuyên khi nào vòng lặp vẫn tốt hơn stream.

14 phút đọc

Bài số № 53

Java cơ bản #10: xử lý ngoại lệ và đọc ghi file với java.nio.file

Khi chương trình gặp lỗi: đọc stack trace, try/catch/finally, checked và unchecked exception, throw và throws, tự định nghĩa ngoại lệ nghiệp vụ, try-with-resources tự đóng tài nguyên; đọc ghi file văn bản và CSV bằng Files, Path, UTF-8 — kèm các thói quen xử lý lỗi nên tránh.

15 phút đọc