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
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
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àiifdù 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
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 ==
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
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ầnbreak. - 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).
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
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:
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
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:
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:
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êntien *= 1.06). Chương trình treo thì bấmCtrl + 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ế.
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ố
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ống | Dù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ến | switch 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ách | for-each (bài 4) |
| Lặp tới khi điều kiện thay đổi | while |
| Phải chạy ít nhất một lần (menu, nhập liệu) | do-while |
Bài tập
- Nhập ba số, in số lớn nhất (không dùng
Math.max). - In các số từ 1 tới 100; số chia hết cho 3 in
Fizz, cho 5 inBuzz, cho cả hai inFizzBuzz— bài phỏng vấn kinh điển. - Nhập
n, kiểm trancó phải số nguyên tố (chỉ cần thử chia tới ). - In tam giác sao cao
ndòng bằng vòng lặp lồng nhau. - 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ọn0để thoát.
Tổng kết
if / else if / elsexé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,yieldcho khối nhiều lệnh. forkhi biết số lần lặp,whilekhi chỉ biết điều kiện dừng,do-whilekhi cần chạy ít nhất một lần.breakthoát vòng lặp,continuebỏ 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.
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.