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 80 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
}
  • break — thoát hẳn vòng lặp: tìm thấy rồi thì tìm tiếp làm gì.

  • continue — bỏ 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 60: Tổng kết chiến lược test

Một chiến lược test cho service thật: mỗi lớp một câu hỏi và tỉ lệ 300 unit, 40 slice, 8 hành trình; fake có hành vi và WireMock ở biên giới; Surefire/Failsafe, Awaitility, Clock và chính sách test chập chờn — cùng checklist 12 câu để review test.

25 thg 9, 202611 phút7
99 Ngày Java — Ngày 60: Stream thực chiến

groupingBy hai cấp với TreeMap, flatMap xuống dòng hàng, top N bằng collectingAndThen, teeing cho min và max, toMap có merge giữ thứ tự — rồi khi nào đặt tên collector, dùng record thay tuple hay quay về vòng for, và một báo cáo từ CSV khép lại Giai đoạn 6.

25 thg 9, 202611 phút7
99 Ngày Spring — Ngày 59: Coverage & chất lượng

JaCoCo ba bước với check làm đỏ build và ngưỡng theo package nghiệp vụ, line khác branch, coverage không đo được assert và PITest chỉ thẳng vào test còn thiếu, kim tự tháp test chạy bằng Surefire và Failsafe — cùng bốn con số đáng theo dõi và những con số nên bỏ.

24 thg 9, 202611 phút9