Java Foundations
22/39
Bài 22 / 39~12 phútĐiều kiện & Vòng lặpLộ trình · chặng 02/21Miễn phí… lượt xem

while và do-while — Lặp khi chưa biết trước số lần

Phân biệt while và do-while trong Java, so sánh cơ chế kiểm tra điều kiện ở bytecode, bẫy vòng lặp vô hạn và cách chọn cấu trúc lặp chuẩn xác.

TL;DR: Vòng lặp while kiểm tra điều kiện trước khi thực thi thân vòng lặp (có thể chạy 0 lần), trong khi do-while luôn thực thi ít nhất một lần rồi mới kiểm tra điều kiện ở cuối. Cả hai đều biên dịch thành khối lệnh nhảy conditional jump ở tầng bytecode, và được ưu tiên khi chưa biết trước số lần lặp.

Khác với rẽ nhánh câu lệnh if-else chỉ chạy đúng 1 lần rồi kết thúc, các tác vụ thực tế thường yêu cầu lặp lại liên tục cho đến khi thỏa mãn điều kiện dừng: đọc từng dòng file đến khi hết, thử lại kết nối mạng (retry), hoặc nhận lệnh từ người dùng đến khi họ gõ quit. Những bài toán không biết trước số vòng lặp chính là đất diễn của while và do-while.

1. Analogy đọc trang sách giúp phân biệt 2 vòng lặp ra sao?

Bạn đọc sách cho đến khi hết:

  • while — Trước khi mở trang, bạn kiểm tra "còn trang không?". Nếu sách 0 trang, bạn không đọc trang nào.
  • do-while — Bạn mở trang đầu tiên rồi mới hỏi "còn nữa không?". Luôn đọc ít nhất 1 trang, dù sách trống.
Đời thườngLoop
Kiểm tra trước khi làmwhile (condition) { body; }
Làm trước, kiểm tra saudo { body; } while (condition);
💡 Cách nhớ
  • while = kiểm tra đầu. Body có thể chạy 0 lần.
  • do-while = kiểm tra cuối. Body luôn chạy ít nhất 1 lần.

2. Vòng lặp while kiểm tra điều kiện trước hoạt động như thế nào?

Cú pháp:

Java
while (<boolean condition>) {
    // body
}

Ví dụ đếm xuống:

Java
int n = 3;
while (n > 0) {
    System.out.println("Count: " + n);
    n--;
}
System.out.println("Done");

Output:

Count: 3
Count: 2
Count: 1
Done

Nếu ban đầu n = 0, body không chạy lần nào — in ngay Done. Đây là điểm khác biệt chính so với do-while.

2.1 Trường hợp dùng while tự nhiên

Java
// Doc file den het
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = reader.readLine()) != null) {
    processLine(line);
}

readLine() trả null khi hết file. Không biết trước file có bao nhiêu dòng → dùng while.

Java
// Retry den khi thanh cong hoac het luot
int attempt = 0;
boolean ok = false;
while (!ok && attempt < 3) {
    ok = callApi();
    attempt++;
}
Java
// Xu ly user input den khi quit
Scanner sc = new Scanner(System.in);
String cmd = "";
while (!cmd.equals("quit")) {
    System.out.print("> ");
    cmd = sc.nextLine();
    runCommand(cmd);
}

Thử ngẫmvòng lặp đọc file trên dùng while vì không biết trước số dòng. Nếu đổi sang lặp trên một mảng đã biết trước độ dài, while còn là lựa chọn tự nhiên nhất không?

3. Vòng lặp do-while kiểm tra điều kiện sau chạy khi nào?

Java
do {
    // body
} while (<boolean condition>);  // chu y dau ;

Body chạy ít nhất 1 lần, rồi kiểm tra điều kiện, lặp nếu đúng.

Java
int n;
Scanner sc = new Scanner(System.in);
do {
    System.out.print("Nhap so tu 1 den 10: ");
    n = sc.nextInt();
} while (n < 1 || n > 10);
System.out.println("OK: " + n);

Đây là use case điển hình: prompt ít nhất 1 lần, re-prompt nếu input sai. Logic tự nhiên hơn viết bằng while.

Đặt hai vòng lặp cạnh nhau với cùng một dữ liệu vào — n = 0, tức điều kiện sai ngay từ đầu — thì khác biệt duy nhất lộ ra: ô kiểm tra nằm trên hay dưới thân.

Hai cột so sánh while và do-while với n bằng 0, cột while thoát ngay còn cột do-while chạy thân một lần rồi mới kiểm tra

Cột phải tụt xuống đúng một bậc, và cái giá của một bậc đó là một lần chạy thân — với input rỗng thì đó chính là ranh giới giữa "không làm gì" và "xử lý một phần tử không tồn tại".

3.1 Vì sao do-while hiếm?

Trong thực tế, 90%+ loop cần kiểm tra trước để tránh chạy oan:

  • Đọc file → kiểm tra có dòng không trước khi xử lý.
  • Duyệt collection → kiểm tra còn element không trước khi next().
  • API retry → kiểm tra chưa hết lượt trước khi gọi.

do-while chỉ tự nhiên khi bước đầu tiên phải chạy (prompt, initialize). Nhiều team thậm chí có coding convention "tránh do-while" vì gây nhầm — reader nhìn do { không thấy điều kiện ngay trên, phải cuộn xuống cuối block mới biết loop chạy khi nào.

Thử ngẫmteam bạn có convention "tránh do-while" vì dễ nhầm. Đoạn prompt nhập số 1–10 phía trên viết lại bằng while thì code có ngắn hơn không, hay bạn phải thêm gì đó?

4. Trình biên dịch JVM sinh bytecode cho vòng lặp như thế nào?

Compiler biên dịch loop thành label + conditional jump + unconditional goto. Ví dụ:

Java
int i = 0;
while (i < 3) {
    System.out.println(i);
    i++;
}

Bytecode (javap -c):

iconst_0
istore_1        // i = 0
L_START:
iload_1
iconst_3
if_icmpge L_END // neu i >= 3 nhay toi L_END
// body
iload_1
invokevirtual println
iinc 1, 1       // i++
goto L_START    // lap lai
L_END:
return

while = kiểm tra trước:

  • L_START → test → nếu fail, nhảy L_END (thoát).
  • Chạy body → goto L_START (quay lại test).

do-while thì đơn giản hơn 1 instruction — bỏ test ở đầu:

L_START:
// body
<test>
if_icmplt L_START  // neu dung, quay lai
// fall through thoat
📚 Vì sao đáng biết?

JIT compiler tối ưu loop rất mạnh — loop unrolling, invariant hoisting, vectorization. Hiểu loop = label + goto giúp bạn đọc được profiler output, biết tại sao thay while thành for có khi đổi hiệu năng (hint cho JIT khác), và tại sao "empty loop" bị optimize bỏ hoàn toàn trong release build.

5. Khi nào nên chủ động thiết kế một vòng lặp vô hạn?

Đôi khi bạn muốn loop chạy mãi, thoát bằng break ở giữa:

Java
while (true) {
    String msg = queue.poll();
    if (msg == null) break;
    handle(msg);
}

Ba cách viết infinite loop trong Java:

Java
while (true) { ... }   // thong dung nhat, ro nghia
for (;;) { ... }        // cu phap tu C, hiem dung trong Java
do { ... } while (true); // hau nhu khong ai viet

Cả 3 compile thành bytecode tương đương. Chọn while (true) vì reader nhìn 1 giây hiểu ngay.

⚠️ Infinite loop ngoài ý muốn — dead lock / freeze

Loop không có điều kiện thoát rõ ràng → treo app:

Java
int i = 0;
while (i < 10) {
    if (i == 5) continue;  // quen i++, i mac ket o 5 mai mai
    doWork(i);
    i++;
}

Tip debug: nếu app đột ngột tốn 100% CPU mà không có log mới, check các loop gần đây. jstack <pid> cho thấy thread đang kẹt ở đâu.

Thử ngẫmproduction CPU đột ngột 100%, không log mới. jstack cho thấy thread kẹt ở một while mới deploy tuần trước. Bạn tìm chỗ thiếu trước — biến đếm quên tăng, hay điều kiện thoát viết sai?

6. Nên chọn while, do-while hay for trong từng tình huống cụ thể?

Tình huốngNên dùngVì sao
Biết trước số lần lặp (0..n-1)for (bài sau)Init/condition/update gọn 1 dòng
Lặp đến khi có điều kiện dừng, chưa biết bao nhiêu lầnwhileKiểm tra trước, an toàn với tập rỗng
Phải chạy ít nhất 1 lần (prompt user)do-whileBody trước, test sau — logic tự nhiên
Loop vô hạn thoát ở giữawhile (true) { ... break; }Rõ ý đồ

7. Các bẫy lỗi thường gặp nhất với vòng lặp while là gì?

❌ Nhầm 1: Quên update biến điều khiển → infinite loop.

Java
int i = 0;
while (i < 10) {
    System.out.println(i);
    // quen i++
}

✅ Đảm bảo biến trong điều kiện có đường được thay đổi trong mỗi vòng.

❌ Nhầm 2: do-while thiếu dấu ; ở cuối.

Java
do { ... } while (cond)   // COMPILE ERROR — thieu ;

✅ Cú pháp do { ... } while (cond); — dấu ; bắt buộc, khác hẳn while bình thường.

❌ Nhầm 3: So sánh double trong điều kiện dừng.

Java
double x = 0;
while (x != 1.0) { x += 0.1; }  // KHONG bao gio dung — floating point

✅ Với float/double, dùng while (x < 1.0) hoặc check Math.abs(x - 1.0) < EPS.

❌ Nhầm 4: Lưu reference vào collection và modify trong khi lặp.

Java
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
int i = 0;
while (i < list.size()) {
    list.remove(0);  // size giam trong khi lap — skip element
    i++;
}

✅ Dùng Iterator.remove() hoặc list.removeIf(...); xem chi tiết ở bài for-each.

8. Tài liệu Deep Dive Oracle quy định gì về vòng lặp?

📚 Deep Dive Oracle (optional)

Spec / reference chính thức:

Ghi chú: Spec ngắn gọn cho while/do-while — 2 loại loop này chỉ khác nhau vị trí test condition. Quan trọng nhất là hiểu semantics "pre-test vs post-test" và tránh infinite loop ngoài ý muốn. Bài tiếp theo về for có spec dày hơn vì có init/condition/update.

9. Điểm cốt lõi cần nhớ về while và do-while là gì?

  • while kiểm tra điều kiện trước; phù hợp nhất cho tập dữ liệu có thể rỗng hoặc chưa biết số lần lặp.
  • do-while luôn chạy thân vòng lặp ít nhất 1 lần rồi mới kiểm tra điều kiện; bắt buộc có dấu ; sau mệnh đề while.
  • Cả hai vòng lặp biên dịch thành cấu trúc label, jump và goto ở bytecode; JIT có thể tối ưu loop unrolling nếu phân tích được biên.
  • Bẫy phổ biến nhất là quên cập nhật biến điều khiển hoặc so sánh trực tiếp số thực float/double gây treo chương trình.

10. Tự kiểm tra kiến thức về while và do-while như thế nào?

Tự kiểm tra
0/5 câu đã trả lời
  1. Q1
    Đoạn sau in gì?
    int n = 0;
    while (n > 0) {
        System.out.println(n);
        n--;
    }
    
    int m = 0;
    do {
        System.out.println(m);
        m--;
    } while (m > 0);
  2. Q2
    Khi nào nên chọn do-while thay vì while?
  3. Q3
    Vì sao while (x != 1.0) x += 0.1; khởi tạo x = 0 có thể chạy vô hạn?
  4. Q4
    Đoạn sau có lỗi gì?
    int i = 0;
    while (i < 10) {
        if (i == 5) continue;
        System.out.println(i);
        i++;
    }
  5. Q5
    Viết 3 cách tạo infinite loop trong Java và chọn cách tốt nhất.

Bài tiếp theo: Vòng lặp for — đếm có chủ đích

Bài này có giúp bạn hiểu bản chất không?

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Lộ trình Junior Java Backend Developer

Chặng 2/21 · Lập trình từ số 0 với Java — bài 22/32 của chặng

Thảo luận & góp ý

Hỏi đáp về bài này

Bài tiếp theo

Vòng lặp for — Kiểm soát số lần lặp và duyệt mảng