Toán tử logic và bitwise — short-circuit, cờ bit và thứ tự ưu tiên
Nắm vững toán tử logic và bitwise trong Java: cơ chế short-circuit null-safe, thao tác cờ bitmask, toán tử ba ngôi và bảng thứ tự ưu tiên biểu thức.
TL;DR: && và || evaluate short-circuit: vế phải bị bỏ qua khi vế trái đã quyết định được kết quả — đây là nền tảng của pattern null-safe s != null && s.isEmpty(). Ngược lại, & và | luôn tính cả hai vế, đồng thời là toán tử bitwise trên số nguyên — công cụ chuẩn cho flags/bitmask. Với shift: >> dịch phải giữ dấu, >>> điền 0 vào bit cao nhất. Ternary ? : gọn cho phép chọn 1-trong-2 giá trị để gán, nhưng nested ternary khó đọc hơn if/else. Và khi không chắc thứ tự ưu tiên toán tử: cứ thêm ngoặc — compiler không tốn thêm chi phí nào.
Bài trước bạn đã nắm toán tử số học, so sánh và gán. Bài này hoàn thành hệ thống toán tử Java với nhóm còn lại: logic (và cơ chế short-circuit đứng sau pattern null-safe kinh điển), bitwise/shift cho thao tác từng bit, ternary cho gán có điều kiện — và cuối cùng là bảng thứ tự ưu tiên để bạn biết Java tính một biểu thức theo trật tự nào.
1. Analogy mạch điện mô tả các toán tử logic và short-circuit như thế nào?
Hãy nghĩ về công tắc mạch điện: hai công tắc nối tiếp (AND) — phải cả hai bật thì đèn mới sáng. Nối song song (OR) — chỉ cần một cái bật. Đó là logical operators.
Và mạch điện có một "thợ điện lười" rất thông minh: ở mạch nối tiếp, thấy công tắc đầu tiên đang tắt thì khỏi cần kiểm tra công tắc thứ hai — đèn chắc chắn không sáng. Đó chính là short-circuit.
| Đời thường | Concept |
|---|---|
| Hai công tắc nối tiếp | && — cả hai vế true mới true |
| Hai công tắc song song | || — một vế true là đủ |
| Thợ điện lười dừng kiểm tra sớm | Short-circuit evaluation |
| Bảng cầu chì bật/tắt từng nhánh | Bitwise flags trên từng bit |
2. Các toán tử logic và cơ chế short-circuit hoạt động ra sao?
| Operator | Ý nghĩa | Khi nào true | Short-circuit |
|---|---|---|---|
&& | AND logic | Cả hai vế true | ✅ Có |
|| | OR logic | Ít nhất một vế true | ✅ Có |
! | NOT logic | Đảo ngược boolean | N/A |
& | AND bitwise/logic | Cả hai vế true | ❌ Không |
| | OR bitwise/logic | Ít nhất một vế true | ❌ Không |
boolean a = true, b = false;
System.out.println(a && b); // false
System.out.println(a || b); // true
System.out.println(!a); // false
2.1 Short-circuit evaluation — bộ não lười
&& và || dừng sớm khi kết quả đã xác định được:
&&: nếu vế trái làfalse→ kết quả chắc chắnfalse→ không evaluate vế phải.||: nếu vế trái làtrue→ kết quả chắc chắntrue→ không evaluate vế phải.

Ứng dụng thực tế — null-safe check:
String s = null;
// AN TOAN: neu s == null thi khong goi s.isEmpty() (short-circuit)
if (s != null && s.isEmpty()) {
System.out.println("Chuoi rong");
}
// NGUY HIEM: & khong short-circuit -- luon evaluate ca hai ve
// if (s != null & s.isEmpty()) // NPE neu s == null!
// Short-circuit trong OR: neu isAdmin() tra true, checkPermission() khong chay
boolean coQuyen = isAdmin() || checkPermission(userId);
&& như bộ não lười khi xét điều kiện AND: "vế đầu đã sai rồi — thôi không cần xem tiếp".
|| cũng lười theo cách khác: "vế đầu đúng rồi — không cần xem tiếp".
Ngược lại & và | (bitwise) luôn tính cả hai vế — dùng khi bạn cần side effect của vế phải xảy ra bất kể kết quả.
2.2 ✅/❌ Khi nào dùng & vs &&?
| Tình huống | Dùng gì | Lý do |
|---|---|---|
| Kết hợp điều kiện boolean thông thường | ✅ && | Short-circuit an toàn hơn, hiệu quả hơn |
| Vế phải là biểu thức có thể null/throw | ✅ && | Tránh NPE khi vế trái đã loại trừ |
| Bitwise operation trên số nguyên | ✅ & | Toán tử bitwise đúng mục đích |
| Cần vế phải LUÔN được evaluate (side effect) | ✅ & | Ít gặp, thường là dấu hiệu cần refactor |
| Điều kiện boolean bình thường | ❌ & | Không short-circuit, không cần thiết |
Thử ngẫmbạn đang review một điều kiện dùng & thay vì && để so hai boolean trong pull request — bạn để reviewer duyệt luôn, hay yêu cầu sửa? Vì sao?
3. Toán tử ba ngôi ternary operator được sử dụng khi nào?
Syntax: condition ? valueIfTrue : valueIfFalse
int diem = 75;
String ketQua = (diem >= 50) ? "Dat" : "Roi";
System.out.println(ketQua); // Dat
// Thay the if/else don gian -- gon hon
int max = (a > b) ? a : b;
Khi nào KHÔNG nên dùng ternary:
// TRANH: nested ternary -- kho doc
String muc = (diem >= 90) ? "Xuat sac" : (diem >= 70) ? "Gioi" : (diem >= 50) ? "Trung binh" : "Roi";
// NEN: if/else if ro rang hon
String muc;
if (diem >= 90) muc = "Xuat sac";
else if (diem >= 70) muc = "Gioi";
else if (diem >= 50) muc = "Trung binh";
else muc = "Roi";
Ternary phù hợp khi chọn 1 trong 2 giá trị đơn giản để gán. Khi logic phức tạp hoặc nested, dùng if/else rõ ràng hơn.
4. Các toán tử bitwise và dịch bit thao tác trên nhị phân như thế nào?
| Operator | Ý nghĩa | Ví dụ | Kết quả |
|---|---|---|---|
& | AND từng bit | 5 & 3 | 1 |
| | OR từng bit | 5 | 3 | 7 |
^ | XOR từng bit | 5 ^ 3 | 6 |
~ | NOT từng bit (complement) | ~5 | -6 |
<< | Left shift | 1 << 3 | 8 |
>> | Arithmetic right shift (giữ dấu) | -8 >> 1 | -4 |
>>> | Logical right shift (điền 0) | -8 >>> 1 | 2147483644 |
int a = 5; // binary: 0101
int b = 3; // binary: 0011
System.out.println(a & b); // 0001 = 1
System.out.println(a | b); // 0111 = 7
System.out.println(a ^ b); // 0110 = 6
System.out.println(~a); // ...11111010 = -6 (two's complement)
System.out.println(1 << 3); // 0001 << 3 = 1000 = 8 (nhan 2^3)
System.out.println(8 >> 1); // 1000 >> 1 = 0100 = 4 (chia 2)
4.1 Use case thực tế — flags và bitmask
Bitwise operators phổ biến khi dùng các bit của một int như tập hợp flags (cờ):
// Dinh nghia flags (moi flag la mot bit rieng)
static final int FLAG_READ = 0b001; // bit 0 = 1
static final int FLAG_WRITE = 0b010; // bit 1 = 2
static final int FLAG_EXECUTE = 0b100; // bit 2 = 4
int quyen = FLAG_READ | FLAG_WRITE; // 0b011 = 3 (co ca READ va WRITE)
// Kiem tra mot flag
boolean coRead = (quyen & FLAG_READ) != 0; // true
boolean coExec = (quyen & FLAG_EXECUTE) != 0; // false
// Bat them flag
quyen |= FLAG_EXECUTE; // them EXECUTE
// Tat flag
quyen &= ~FLAG_WRITE; // xoa WRITE (AND voi NOT WRITE)
// Toggle flag
quyen ^= FLAG_READ; // bat neu tat, tat neu bat
// Kiem tra so chan: bit cuoi == 0
boolean laSoChan = (n & 1) == 0;
// Chia doi nhanh bang shift (chi dung voi so duong)
int halved = n >> 1; // tuong duong n / 2 nhung nhanh hon
4.2 >> vs >>> — giữ dấu hay không?
int negative = -8; // binary: 11111111111111111111111111111000
System.out.println(negative >> 1); // -4: dich phai giu bit dau (1)
System.out.println(negative >>> 1); // 2147483644: dich phai dien 0 vao bit cao nhat
>>(arithmetic): giữ nguyên dấu — dùng khi chia 2 số nguyên có dấu.>>>(logical): luôn điền 0 — dùng khi cần xử lý bit pattern thô (hash function, network protocol), bất kể dấu.
Thử ngẫmhệ thống phân quyền của bạn đang dùng số nguyên làm bitmask cho dưới 32 quyền — nếu số quyền vượt 32, thiết kế này còn đứng vững không, và bạn chuyển sang gì?
5. Thứ tự ưu tiên toán tử trong Java được quy định ra sao?
Java evaluate theo thứ tự ưu tiên từ cao đến thấp. Các operator cùng mức thực hiện từ trái sang phải (trừ assignment từ phải sang trái):
| Mức (cao → thấp) | Operators |
|---|---|
| 1 — Postfix | x++, x-- |
| 2 — Prefix / Unary | ++x, --x, +x, -x, ~, ! |
| 3 — Multiplicative | *, /, % |
| 4 — Additive | +, - |
| 5 — Shift | <<, >>, >>> |
| 6 — Relational | <, >, <=, >=, instanceof |
| 7 — Equality | ==, != |
| 8 — Bitwise AND | & |
| 9 — Bitwise XOR | ^ |
| 10 — Bitwise OR | | |
| 11 — Logical AND | && |
| 12 — Logical OR | || |
| 13 — Ternary | ? : |
| 14 — Assignment | =, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, |= |
// Khong ngoac -- dua vao precedence
int result = 2 + 3 * 4; // = 14 (3*4 truoc, roi cong 2)
// Ro rang hon voi ngoac
int result2 = 2 + (3 * 4); // = 14 -- cung ket qua, ro hon
int result3 = (2 + 3) * 4; // = 20 -- thay doi thu tu
// Ket hop comparison va logical
boolean check = x > 0 && x < 100; // > va < truoc &&
Khi nghi ngờ, dùng dấu ngoặc. Code có ngoặc rõ ràng hơn là code "đúng nhờ nhớ thứ tự". Ngoặc không tốn gì về performance — compiler optimize hết.
Thử ngẫmsếp bạn yêu cầu code "ngắn gọn, ít ngoặc thừa" — khi nào bạn vẫn cố tình thêm ngoặc dù compiler không bắt buộc, và lý do đó có thắng được yêu cầu kia không?
6. Mã nguồn ví dụ minh họa logic và bitwise hoạt động như thế nào?
public class LogicBitwiseDemo {
static final int FLAG_READ = 0b001;
static final int FLAG_WRITE = 0b010;
static final int FLAG_EXECUTE = 0b100;
public static void main(String[] args) {
// --- Short-circuit null safe ---
String s = null;
if (s != null && s.isEmpty()) {
System.out.println("Chuoi rong");
} else {
System.out.println("s la null hoac co noi dung");
}
// --- Bitwise flags ---
int permissions = FLAG_READ | FLAG_WRITE;
System.out.println("Co READ: " + ((permissions & FLAG_READ) != 0)); // true
System.out.println("Co EXECUTE: " + ((permissions & FLAG_EXECUTE) != 0)); // false
// Kiem tra so chan bang bitwise
for (int i = 0; i < 6; i++) {
System.out.println(i + " la so " + ((i & 1) == 0 ? "chan" : "le"));
}
// --- Shift: giu dau vs dien 0 ---
int negative = -8;
System.out.println(negative >> 1); // -4
System.out.println(negative >>> 1); // 2147483644
// --- Ternary ---
int score = 75;
String result = (score >= 50) ? "Pass" : "Fail";
System.out.println("Ket qua: " + result);
}
}
7. Bảng tổng hợp các toán tử logic và bitwise gồm những gì?
| Nhóm | Operators | Operand | Trả về | Ghi chú |
|---|---|---|---|---|
| Logical | && || ! | boolean | boolean | Short-circuit |
| Bitwise/logical | & | ^ ~ | int/long/boolean | int/long/boolean | Không short-circuit |
| Shift | << >> >>> | int/long | int/long | >> giữ dấu, >>> điền 0 |
| Ternary | ? : | boolean + any | same as branches | Gán có điều kiện gọn |
8. Tài liệu Deep Dive Oracle quy định gì về logic và bitwise?
Spec chính thức Java 21:
- JLS §15.19 — Shift Operators:
<<,>>,>>>— quy tắc điền bit và shift distance. - JLS §15.22 — Bitwise and Logical Operators:
&,|,^— cả dạng boolean và integer. - JLS §15.23 — Conditional-And Operator && và §15.24 — Conditional-Or Operator ||: spec hoá short-circuit — vế phải "is not evaluated" khi vế trái đã quyết định.
- JLS §15.25 — Conditional Operator ? :: quy tắc type của ternary expression.
Diễn giải đơn giản: §15.23/§15.24 nói rõ short-circuit là hành vi được spec đảm bảo, không phải optimization tuỳ JVM — bạn có thể dựa vào nó cho null-safe check. §15.22 giải thích vì sao &/| dùng được cho cả boolean lẫn integer: với boolean chúng là logic không short-circuit, với integer chúng là phép AND/OR trên từng bit.
Đọc thêm: Byte order và thao tác bit — góc nhìn phần cứng của &, |, ^, shift: vì sao chúng là phép toán rẻ nhất mà CPU thực thi trực tiếp trên từng bit.
9. Điểm cốt lõi cần nhớ về logic, bitwise và thứ tự ưu tiên là gì?
- Cơ chế Short-circuit:
&&và||bỏ qua vế phải khi vế trái đã chốt kết quả, cho phép viết an toàns != null && s.isEmpty(). - Thao tác cờ bitmask: Bật bit bằng
|=, kiểm tra bằng(x & FLAG) != 0, tắt bit bằng&= ~FLAG, và đảo bit bằng^=. - Dịch bit giữ dấu vs điền 0:
>>bảo toàn bit dấu âm, trong khi>>>luôn điền 0 vào vị trí cao nhất. - Thứ tự ưu tiên: Không cố nhớ toàn bộ bảng ưu tiên; luôn bao bọc các nhóm biểu thức con bằng dấu ngoặc
().
10. Tự kiểm tra kiến thức về toán tử logic và bitwise như thế nào?
- Q1Đoạn sau an toàn hay có thể NPE? Vì sao?
String s = getInput(); // co the tra null if (s != null && s.length() > 0) { ... } - Q2Khi nào dùng
&thay vì&&với hai vế boolean? Vì sao trường hợp đó hiếm gặp? - Q3
(quyen & FLAG_READ) != 0làm gì? Giải thích từng bước bitwise. - Q4Với
int negative = -8;—negative >> 1vànegative >>> 1cho kết quả khác nhau thế nào? Vì sao>>>ra một số dương rất lớn? - Q5Cho biết:
2 + 3 * 4= bao nhiêu? Tại sao không phải20?
Bài tiếp theo: Ép kiểu — widening, narrowing, autoboxing và safe cast patterns
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.
Toán tử số học và so sánh — phép chia nguyên, modulo âm và bẫy ==
Bài tiếpÉp kiểu trong Java: Widening, Narrowing và Safe Cast
Lộ trình Junior Java Backend Developer
Chặng 2/21 · Lập trình từ số 0 với Java — bài 14/32 của chặng
Thảo luận & góp ý
Hỏi đáp về bài này