99 Ngày Java — Ngày 06: Câu lệnh điều kiện if & switch
Năm ngày qua, chương trình của ta chạy thẳng một mạch từ dòng đầu tới dòng cuối. Hôm nay code học kỹ năng quan trọng nhất của mọi ngôn ngữ: rẽ nhánh. Ta đi từ if–else if quen thuộc, qua guard clause giúp code phẳng dễ đọc, đến switch — cả bản cổ điển đầy bẫy lẫn switch expression hiện đại mà Java 21 khuyến khích dùng.

if – else if: chạy nhánh đúng đầu tiên rồi dừng
Điều kiện trong ngoặc bắt buộc là boolean — chính là các biểu thức so sánh và logic của Ngày 04; khác với C, số nguyên không tự biến thành đúng/sai. Java xét các nhánh từ trên xuống, gặp điều kiện đúng đầu tiên là chạy nhánh đó rồi bỏ qua toàn bộ phần còn lại:
int diem = 82;
if (diem >= 90) {
System.out.println("Xuất sắc");
} else if (diem >= 75) {
System.out.println("Giỏi"); // ← 82 rơi vào đây rồi DỪNG
} else if (diem >= 60) {
System.out.println("Khá"); // 82 >= 60 cũng đúng — nhưng không bao giờ xét tới
} else {
System.out.println("Cần cố gắng");
}Với diem = 82, cả >= 75 lẫn >= 60 đều đúng — nhưng chỉ "Giỏi" được in vì nhánh đứng trước thắng. Bài học: xếp điều kiện từ chặt đến lỏng; đảo thứ tự là 82 thành "Khá" ngay.
Guard clause — chặn sớm, thoát sớm
Cứ mỗi tầng if lồng nhau, người đọc phải giữ thêm một điều kiện trong đầu — lồng 3–4 tầng thành "mũi tên chết chóc" trứ danh. Guard clause đảo ngược tư duy: kiểm tra trường hợp xấu trước và thoát ngay, để luồng chính nằm phẳng ở cuối:
// if lồng 3 tầng — "mũi tên chết chóc", đọc mệt
if (user != null) {
if (user.isActive()) {
if (user.hasPermission()) {
doWork();
}
}
}
// Guard clause — chặn sớm, thoát sớm, luồng chính nằm phẳng
if (user == null) return;
if (!user.isActive()) return;
if (!user.hasPermission()) return;
doWork();return sống trong method — chủ đề của Ngày 10 — nhưng nguyên tắc thì dùng được từ hôm nay: việc đặc biệt xử trước, việc chính để phẳng. Đây là một trong những thói quen tách code dễ đọc khỏi code "leo thang".
switch cổ điển — và cái bẫy fall-through
Khi so sánh một giá trị với nhiều hằng số, chuỗi else if dài lê thê — switch sinh ra cho việc này. Nhưng bản cổ điển mang cái bẫy nổi tiếng nhất lịch sử Java: quên break là thực thi "rơi xuyên" (fall-through) xuống case kế tiếp:
int thu = 3;
switch (thu) {
case 2:
System.out.println("Đầu tuần");
break; // quên break là "rơi xuyên" xuống case dưới!
case 3:
case 4:
case 5:
System.out.println("Giữa tuần");
break;
case 6:
System.out.println("Sắp cuối tuần");
break;
default:
System.out.println("Cuối tuần");
}Switch cổ điển nhận
int,char,String, enum (Ngày 23)…Fall-through đôi khi hữu ích — gộp
case 3: case 4: case 5:như trên — nhưng đa số lần xuất hiện là bug.defaultkhông bắt buộc, nhưng nên có để đỡ "im lặng khó hiểu" khi giá trị lạ lọt vào.
switch expression — switch đời mới trả về giá trị
Từ Java 14, switch còn là biểu thức: dùng mũi tên ->, không fall-through, và trả thẳng giá trị để gán vào biến:
int thu = 3;
String loai = switch (thu) {
case 2 -> "Đầu tuần";
case 3, 4, 5 -> "Giữa tuần"; // nhiều nhãn trên một dòng
case 6 -> "Sắp cuối tuần";
case 7, 1 -> "Cuối tuần";
default -> {
System.out.println("Thứ lạ: " + thu);
yield "Không rõ"; // trong block, trả giá trị bằng yield
}
};
System.out.println(loai); // Giữa tuầnMũi tên
->không rơi xuyên — mỗi case độc lập, không cần break.Nhiều nhãn chung một nhánh:
case 3, 4, 5 ->— gọn hơn hẳn bản cổ điển.Nhánh cần nhiều câu lệnh thì mở block
{ }và trả giá trị bằngyield.Switch expression bắt buộc đủ mọi trường hợp (exhaustive) — thiếu là lỗi compile; kết hợp enum sẽ thấy sức mạnh thật sự ở Ngày 23, còn pattern matching hẹn Ngày 82.
Lời khuyên đơn giản: mặc định dùng switch expression — an toàn hơn, gọn hơn; chỉ gặp bản cổ điển khi đọc code cũ.
Ba cái bẫy kinh điển
// Bẫy 1: if không ngoặc — chỉ câu lệnh ĐẦU TIÊN thuộc if
if (diem >= 60)
System.out.println("Đậu");
System.out.println("Chúc mừng!"); // LUÔN chạy — thụt lề đánh lừa mắt!
// Bẫy 2: không có so sánh khoảng kiểu toán học
// if (0 <= diem <= 100) // ❌ không compile — phải tách:
if (diem >= 0 && diem <= 100) { } // ✓
// Bẫy 3: gán nhầm trong điều kiện boolean
boolean dangMo = false;
if (dangMo = true) { } // gán, không phải so sánh — luôn true!
// May mắn: với số, if (x = 5) không compile vì điều kiện phải là booleanBẫy 1 nguy hiểm tới mức nhiều team quy định luôn mở ngoặc nhọn kể cả một câu lệnh — thụt lề không có ý nghĩa với compiler. Bẫy 2 là phản xạ từ toán học mà Java không cho phép: khoảng giá trị phải tách bằng &&. Bẫy 3 hiếm nhưng cay: với biến boolean, dấu = lọt qua compiler và điều kiện luôn đúng — thêm một lý do để so sánh kiểu if (dangMo) thay vì if (dangMo == true).
Bài tập nhỏ
Viết
if–else ifxếp loại điểm 0–10: dưới 5 yếu, dưới 6.5 trung bình, dưới 8 khá, còn lại giỏi — rồi thử đảo thứ tự các nhánh xem kết quả sai thế nào.Dùng switch expression đổi số tháng 1–12 thành số ngày trong tháng (tháng 2 cứ trả 28) — các tháng 31 ngày gộp chung một nhãn.
Xóa hết
breaktrong ví dụ switch cổ điển vớithu = 3— đoán trước xem in ra những dòng nào, rồi chạy kiểm chứng.Viết lại một đoạn if lồng 3 tầng bất kỳ của bạn bằng guard clause.
Kết luận
Code của bạn giờ biết rẽ nhánh: if–else if chạy nhánh đúng đầu tiên, guard clause giữ luồng chính phẳng, switch cổ điển cần break còn switch expression an toàn và trả giá trị. Ngày 07, mảnh ghép còn thiếu của điều khiển luồng: vòng lặp for, while, do-while — code không chỉ biết rẽ mà còn biết lặp. Hẹn gặp lại!
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.


