Backend

99 Ngày Java — Ngày 07: Vòng lặp for, while, do-while

SSite Admin
4 tháng 08, 2026 6 phút đọc 44 lượt xem
99 Ngày Java — Ngày 07: Vòng lặp for, while, do-while

Hôm qua code học rẽ nhánh; hôm nay nó học lặp lại — mảnh ghép thứ hai và cũng là mảnh cuối của điều khiển luồng. Ba anh em for, while, do-while mỗi người một việc, cộng thêm break/continue để lái dòng chảy, label cho vòng lặp lồng nhau — và ba cái bẫy khiến vòng lặp chạy sai hoặc… chạy mãi không dừng.

Sketchnote Ngày 07: vòng lặp for, while, do-while trong Java

for — biết trước số lần lặp

Khi biết trước cần lặp bao nhiêu lần, for gói cả ba việc — khởi tạo, điều kiện, bước nhảy — vào một dòng:

// for: biết trước số lần lặpfor (int i = 1; i <= 5; i++) {
    System.out.println("Lần chạy thứ " + i);
}
// ① int i = 1   — khởi tạo, chạy MỘT lần duy nhất// ② i <= 5      — kiểm tra TRƯỚC mỗi vòng; sai là thoát// ③ i++         — bước nhảy, chạy SAU thân vòng lặp// Thứ tự: ① → ② → thân → ③ → ② → thân → ③ → … → ② sai → thoát

Nhớ kỹ thứ tự: điều kiện được kiểm tra trước mỗi lần chạy thân, còn bước nhảy chạy sau. Biến i khai báo trong for chỉ sống trong vòng lặp — ra ngoài là hết phạm vi (đúng tinh thần phạm vi biến của Ngày 03). Với mảng và collection còn có for-each (for (int x : mang)) — gọn hơn hẳn khi chỉ cần duyệt qua từng phần tử; ta sẽ dùng nó dày đặc từ Ngày 08.

while & do-while — lặp theo điều kiện

Khi không biết trước số lần — đọc dữ liệu tới khi hết, chờ người dùng nhập đúng — while là lựa chọn tự nhiên. Khác biệt duy nhất giữa hai anh em: while kiểm tra trước (có thể không chạy lần nào), do-while kiểm tra sau (thân luôn chạy ít nhất một lần):

// while: lặp khi CHƯA biết trước số lần — kiểm tra trướcint soDu = 1000;
while (soDu >= 200) {
    soDu -= 200;              // rút 200 mỗi lần
}
System.out.println(soDu);     // 0

// do-while: chạy thân ÍT NHẤT MỘT lần, kiểm tra sauint luaChon;
do {
    luaChon = docLuaChonTuBanPhim();   // menu luôn hiện ít nhất 1 lần
} while (luaChon != 0);

Mẹo chọn nhanh: menu, nhập liệu có xác nhận → do-while; mọi trường hợp "lặp khi còn…" khác → while; đếm số lần rõ ràng → for.

break & continue — lái dòng chảy giữa chừng

int[] mang = {4, 9, 7, 12, 5};

// break: tìm thấy là DỪNG cả vòng lặpfor (int x : mang) {
    if (x > 10) {
        System.out.println("Gặp " + x + " — dừng!");
        break;                // thoát hẳn vòng lặp
    }
}

// continue: BỎ QUA phần còn lại của lần này, sang lần kếfor (int x : mang) {
    if (x % 2 == 0) continue; // chẵn thì bỏ qua
    System.out.println(x);    // chỉ in số lẻ: 9 7 5
}
  • breakthoát hẳn vòng lặp: tìm thấy rồi thì tìm tiếp làm gì.

  • continuebỏ qua lần này, nhảy sang lần kế: lọc phần tử không cần xử lý.

  • Cả hai chính là guard clause phiên bản vòng lặp (Ngày 06): xử sớm trường hợp đặc biệt, thân vòng lặp nằm phẳng.

Vòng lặp lồng nhau & label

Duyệt bảng 2 chiều là gặp ngay vòng lặp lồng nhau: vòng ngoài chạy 1 lần thì vòng trong chạy trọn bộ số lần của nó. Vấn đề: break mặc định chỉ thoát vòng gần nhất. Muốn thoát cả cụm, đặt label trước vòng ngoài:

// Vòng lặp lồng nhau + label: break/continue nhắm vòng NGOÀI
tim:                                  // nhãn đặt trước vòng ngoàifor (int hang = 1; hang <= 3; hang++) {
    for (int cot = 1; cot <= 3; cot++) {
        if (hang * cot == 6) {
            System.out.println(hang + " x " + cot);
            break tim;                // thoát CẢ HAI vòng — không chỉ vòng trong
        }
    }
}
// Không có label, break chỉ thoát vòng TRONG — vòng ngoài vẫn chạy tiếp

Label hợp lệ nhưng hiếm khi nên lạm dụng — lồng sâu quá 2 tầng thường là dấu hiệu nên tách vòng trong thành method riêng (Ngày 10). Gặp label trong code cũ thì đọc hiểu được là đủ.

Ba cái bẫy kinh điển

// Bẫy 1: dấu chấm phẩy thừa — thân vòng lặp là câu lệnh RỖNGfor (int i = 0; i < 5; i++);          // ← dấu ; này "nuốt" thân!
    System.out.println("Chỉ in 1 lần");

// Bẫy 2: quên cập nhật biến điều khiển → lặp vô hạnint i = 0;
while (i < 5) {
    System.out.println(i);            // quên i++ → chạy mãi không dừng
}

// Bẫy 3: sửa điều kiện biên sai 1 đơn vị (off-by-one)for (int k = 0; k <= 5; k++) { }      // chạy 6 lần, không phải 5!

Bẫy 1 tàn khốc vì code vẫn compile — dấu ; ngay sau ) là một câu lệnh rỗng, thụt lề bên dưới chỉ đánh lừa mắt người (bài học quen thuộc từ Ngày 06). Bẫy 2 là vòng lặp vô hạn — treo chương trình, CPU 100%; trước khi viết while hãy tự hỏi "cái gì làm điều kiện này sai đi?". Bẫy 3 — off-by-one — là lỗi phổ biến nhất khi làm việc với mảng: < hay <= quyết định chạy 5 hay 6 lần, và Ngày 08 bạn sẽ thấy nó gây ArrayIndexOutOfBoundsException thế nào.

Bài tập nhỏ

  • In bảng cửu chương 7 bằng for, rồi in cả 9 bảng bằng hai vòng lặp lồng nhau.

  • Dùng while tính xem gửi 100 triệu lãi kép 6%/năm thì sau bao nhiêu năm vượt 200 triệu.

  • Duyệt mảng {3, 8, -2, 7, -5}: dùng continue bỏ qua số âm, break dừng khi tổng vượt 15 — đoán kết quả trước khi chạy.

  • Cố tình viết một vòng lặp vô hạn, quan sát rồi sửa — biết "mùi" của nó để sau này nhận ra ngay.

Kết luận

Điều khiển luồng đã đủ bộ: rẽ nhánh hôm qua, lặp hôm nay — for khi biết số lần, while/do-while khi lặp theo điều kiện, break/continue lái giữa chừng. Ngày 08 ta gặp người bạn đồng hành ăn ý nhất của vòng lặp: mảng (arrays) — nơi off-by-one hết chỗ trốn. Hẹn gặp lại!

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Spring — Ngày 14: Bean Validation — @Valid & bộ constraint chuẩn

Ngày 14 của 99 Ngày Spring: Bean Validation khai báo luật ngay trên DTO — @Valid kích hoạt, bộ constraint chuẩn (@NotBlank vs @NotEmpty vs @NotNull, @Size, @Email, @Min/@Max), bẫy int vs Integer, validate object lồng nhau không tự lan, và lỗi 400 gom một lượt.

10 thg 8, 20265 phút0
99 Ngày Java — Ngày 14: Kế thừa (Inheritance)

Ngày 14 của 99 Ngày Java: extends trao gia tài từ lớp cha, override với @Override và super., thứ tự khai sinh cha trước con sau qua super(...), protected trả nợ Ngày 13, và phép thử is-a vs has-a — khi nào nên composition thay vì kế thừa.

10 thg 8, 20266 phút0
99 Ngày Spring — Ngày 13: @RequestBody & DTO — đừng công khai entity ra API

Ngày 13 của 99 Ngày Spring: @RequestBody + Jackson deserialize JSON thành object, DTO là hợp đồng API — chặn lỗ hổng mass assignment chiều vào và rò rỉ dữ liệu chiều ra, request/response DTO khác nhau là bình thường, và Jackson annotations tinh chỉnh hợp đồng JSON.

9 thg 8, 20266 phút9