Toán tử logic và bitwise — short-circuit, cờ bit và thứ tự ưu tiên
Vì sao && và || dừng sớm (short-circuit) và đó là pattern null-safe chuẩn của Java; bitwise/shift cho flags và bitmask; ternary cho gán có điều kiện; bảng operator precedence và quy tắc 'khi nghi ngờ, dùng ngoặ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 — Công tắc mạch điện
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. Logical operators — kết hợp điều kiện
| 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 |
3. Ternary operator ? : — gán có điều kiện
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. Bitwise operators — thao tác trên từng bit
| 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.
5. Operator precedence — thứ tự ưu tiên
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.
6. Code example — demo tổng hợp
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 nhóm toán tử bài này
| 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. Deep Dive Oracle
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.
9. Tóm tắt
&&/||short-circuit: vế phải không evaluate nếu kết quả đã xác định — spec đảm bảo, là nền của pattern null-safes != null && s.isEmpty().- Thứ tự vế quan trọng: điều kiện "gác cổng" (null check) phải đứng bên trái.
&/|không short-circuit: evaluate cả hai vế — vai trò chính là bitwise operator cho số nguyên.- Flags/bitmask: bật flag bằng
|=, kiểm tra bằng(x & FLAG) != 0, tắt bằng&= ~FLAG, toggle bằng^=. >>giữ dấu (chia 2 số có dấu),>>>điền 0 (xử lý bit pattern thô).- Ternary
? :gọn cho gán 1-trong-2; nested ternary → đổi sangif/else. - Khi nghi ngờ precedence: dùng ngoặc.
10. Tự kiểm tra
- 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 đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên