Toán tử (Operators)
Sau bài này, bạn sẽ:
- Sử dụng thành thạo các toán tử arithmetic, assignment, comparison, logical và bitwise
- Hiểu operator precedence và tránh các lỗi logic phổ biến
- Phân biệt short-circuit (
&&,||) và non-short-circuit (&,|) operators - Sử dụng ternary operator đúng cách và hiểu type inference rules
Bài trước: Type Casting và var Keyword — Đã học về type casting và var keyword. Bài này sẽ học cách sử dụng các toán tử trong Java.
Operators là ký hiệu để thực hiện các phép toán trên operands (toán hạng).
int result = 10 + 5;
// ↑ ↑ ↑
// operand operator operand
1. Arithmetic Operators (Toán tử số học)
| Operator | Tên | Ví dụ | Kết quả |
|---|---|---|---|
+ | Addition (Cộng) | 5 + 3 | 8 |
- | Subtraction (Trừ) | 5 - 3 | 2 |
* | Multiplication (Nhân) | 5 * 3 | 15 |
/ | Division (Chia) | 5 / 2 | 2 (integer division) |
% | Modulus (Chia lấy dư) | 5 % 2 | 1 |
Ví dụ:
int a = 10;
int b = 3;
System.out.println(a + b); // 13
System.out.println(a - b); // 7
System.out.println(a * b); // 30
System.out.println(a / b); // 3 (integer division, mất phần thập phân)
System.out.println(a % b); // 1 (10 = 3*3 + 1)
Khi chia 2 integers, kết quả là integer (mất phần thập phân)!
int x = 5;
int y = 2;
int result = x / y; // 2 (không phải 2.5)
double result2 = x / y; // 2.0 (vẫn là 2!)
double result3 = (double) x / y; // 2.5 ✅ (cast to double)
double result4 = 5.0 / 2; // 2.5 ✅ (literal double)
Modulus use cases:
// Check even/odd
int num = 7;
if (num % 2 == 0) {
System.out.println("Even");
} else {
System.out.println("Odd"); // Output: Odd
}
// Get last digit
int number = 12345;
int lastDigit = number % 10; // 5
// Cycle through range (0-6)
for (int i = 0; i < 20; i++) {
int day = i % 7; // 0,1,2,3,4,5,6,0,1,2,...
}
2. Unary Operators (Toán tử một ngôi)
| Operator | Tên | Ví dụ | Kết quả |
|---|---|---|---|
+ | Unary plus | +5 | 5 |
- | Unary minus (Đổi dấu) | -5 | -5 |
++ | Increment (Tăng 1) | x++ hoặc ++x | Tăng x lên 1 |
-- | Decrement (Giảm 1) | x-- hoặc --x | Giảm x xuống 1 |
! | Logical NOT | !true | false |
Increment/Decrement:
int x = 5;
// Post-increment: Dùng giá trị CŨ, sau đó tăng
int a = x++; // a = 5, x = 6
System.out.println("a: " + a + ", x: " + x); // a: 5, x: 6
x = 5;
// Pre-increment: Tăng TRƯỚC, sau đó dùng giá trị mới
int b = ++x; // x = 6, b = 6
System.out.println("b: " + b + ", x: " + x); // b: 6, x: 6
Ví dụ so sánh:
int i = 10;
System.out.println(i++); // In 10, sau đó i = 11
System.out.println(i); // In 11
i = 10;
System.out.println(++i); // i = 11, sau đó in 11
System.out.println(i); // In 11
Tránh dùng increment/decrement trong expressions phức tạp - dễ gây confuse!
// ❌ BAD - Confusing
int x = 5;
int y = x++ + ++x; // Khó hiểu!
// ✅ GOOD - Clear
int x = 5;
x++;
x++;
int y = x + x;
3. Assignment Operators (Toán tử gán)
| Operator | Tương đương | Ví dụ | Kết quả |
|---|---|---|---|
= | Gán | x = 5 | x = 5 |
+= | x = x + y | x += 3 | x = x + 3 |
-= | x = x - y | x -= 3 | x = x - 3 |
*= | x = x * y | x *= 3 | x = x * 3 |
/= | x = x / y | x /= 3 | x = x / 3 |
%= | x = x % y | x %= 3 | x = x % 3 |
Ví dụ:
int x = 10;
x += 5; // x = x + 5 = 15
x -= 3; // x = x - 3 = 12
x *= 2; // x = x * 2 = 24
x /= 4; // x = x / 4 = 6
x %= 4; // x = x % 4 = 2
System.out.println(x); // 2
String concatenation:
String str = "Hello";
str += " World"; // str = str + " World"
System.out.println(str); // "Hello World"
4. Comparison Operators (Toán tử so sánh)
Trả về boolean (true hoặc false).
| Operator | Tên | Ví dụ | Kết quả |
|---|---|---|---|
== | Equal to | 5 == 5 | true |
!= | Not equal | 5 != 3 | true |
> | Greater than | 5 > 3 | true |
< | Less than | 5 < 3 | false |
>= | Greater than or equal | 5 >= 5 | true |
<= | Less than or equal | 5 <= 3 | false |
Ví dụ:
int a = 10;
int b = 20;
System.out.println(a == b); // false
System.out.println(a != b); // true
System.out.println(a > b); // false
System.out.println(a < b); // true
System.out.println(a >= 10); // true
System.out.println(a <= 5); // false
KHÔNG dùng == để so sánh Strings!
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2); // true (cùng object trong pool)
System.out.println(s1 == s3); // false (khác objects!)
// ✅ ĐÚNG: Dùng .equals()
System.out.println(s1.equals(s3)); // true (so sánh content)
5. Logical Operators (Toán tử logic)
Kết hợp nhiều điều kiện boolean.
| Operator | Tên | Mô tả | Ví dụ | Kết quả |
|---|---|---|---|---|
&& | AND | Cả 2 đều true | true && false | false |
|| | OR | Ít nhất 1 true | true || false | true |
! | NOT | Đảo ngược | !true | false |
Truth tables:
AND (&&):
| A | B | A && B |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
OR (||):
| A | B | A || B |
|---|---|---|
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |
NOT (!):
| A | !A |
|---|---|
| true | false |
| false | true |
Ví dụ:
int age = 25;
boolean hasLicense = true;
// AND: Cả 2 điều kiện đều phải true
if (age >= 18 && hasLicense) {
System.out.println("Có thể lái xe");
}
// OR: Ít nhất 1 điều kiện true
boolean isWeekend = true;
if (age < 18 || isWeekend) {
System.out.println("Giảm giá vé xem phim");
}
// NOT: Đảo ngược
if (!hasLicense) {
System.out.println("Không được lái xe");
}
Short-circuit evaluation:
&& và || có short-circuit behavior — không evaluate right operand nếu không cần thiết.
Operator &&:
// AND: Nếu left = false → không evaluate right
int x = 5;
if (x < 0 && x / 0 > 10) { // x < 0 = false → không chạy x/0 (tránh lỗi!)
// ...
}
Operator ||:
// OR: Nếu left = true → không evaluate right
if (x > 0 || x / 0 > 10) { // x > 0 = true → không chạy x/0
System.out.println("True");
}
Short-circuit vs Non-short-circuit
Java có 2 loại logical operators:
| Operator | Name | Short-circuit? | Use case |
|---|---|---|---|
&& | Logical AND | ✅ Yes | Most common — safe, efficient |
& | Bitwise AND | ❌ No | Bitwise operations, hoặc cần evaluate cả 2 sides |
|| | Logical OR | ✅ Yes | Most common — safe, efficient |
| | Bitwise OR | ❌ No | Bitwise operations, hoặc cần evaluate cả 2 sides |
Ví dụ sự khác biệt:
int x = 5;
int y = 0;
// Short-circuit && - Right side NOT evaluated
if (y != 0 && x / y > 2) { // y != 0 = false → STOP, không chạy x/y
System.out.println("OK");
}
// ✅ No exception!
// Non-short-circuit & - Right side ALWAYS evaluated
if (y != 0 & x / y > 2) { // y != 0 = false, NHƯNG vẫn evaluate x/y
System.out.println("OK");
}
// ❌ ArithmeticException: / by zero
Khi nào dùng &, | thay vì &&, ||?
- Side effects cần thực thi:
// Muốn increment cả 2 counters
if (++counter1 > 5 && ++counter2 > 10) { } // counter2 có thể không increment!
if (++counter1 > 5 & ++counter2 > 10) { } // ✅ Cả 2 đều increment
- Bitwise operations:
int flags = 0b1010;
int mask = 0b0110;
int result = flags & mask; // 0b0010 (bitwise AND)
Case 1: NullPointerException avoidance
String text = null;
// ❌ NullPointerException
if (text.length() > 0 && text.contains("Java")) { }
// ✅ Safe with short-circuit
if (text != null && text.length() > 0) { // text != null = false → STOP
// Safe to use text here
}
Case 2: Array bounds checking
int[] arr = {1, 2, 3};
int index = 5;
// ✅ Safe with short-circuit
if (index < arr.length && arr[index] > 0) { // index < arr.length = false → STOP
// No ArrayIndexOutOfBoundsException
}
// ❌ Crash without short-circuit
if (index < arr.length & arr[index] > 0) { // Still evaluates arr[index]!
// ArrayIndexOutOfBoundsException
}
Case 3: Method calls with side effects
boolean checkUsername(String name) {
System.out.println("Checking username: " + name);
return name.length() > 5;
}
boolean checkEmail(String email) {
System.out.println("Checking email: " + email);
return email.contains("@");
}
// Short-circuit &&
if (checkUsername("Bob") && checkEmail("[email protected]")) { }
// Output: Checking username: Bob
// (checkEmail NOT called because checkUsername returns false)
// Non-short-circuit &
if (checkUsername("Bob") & checkEmail("[email protected]")) { }
// Output:
// Checking username: Bob
// Checking email: [email protected]
// (Both methods called!)
&&: "Nếu left = false, tôi biết kết quả rồi → không cần check right"||: "Nếu left = true, tôi biết kết quả rồi → không cần check right"- Giống như bạn lazy — làm ít nhất có thể!
Complex conditions:
int score = 85;
boolean hasExtraCredit = true;
if ((score >= 90) || (score >= 80 && hasExtraCredit)) {
System.out.println("Grade: A");
}
6. Bitwise Operators (Toán tử bit)
Thao tác trực tiếp trên bits.
| Operator | Tên | Ví dụ | Kết quả |
|---|---|---|---|
& | Bitwise AND | 5 & 3 | 1 (0101 & 0011 = 0001) |
| | Bitwise OR | 5 | 3 | 7 (0101 | 0011 = 0111) |
^ | Bitwise XOR | 5 ^ 3 | 6 (0101 ^ 0011 = 0110) |
~ | Bitwise NOT | ~5 | -6 (invert bits) |
<< | Left shift | 5 << 1 | 10 (0101 << 1 = 1010) |
>> | Right shift | 5 >> 1 | 2 (0101 >> 1 = 0010) |
>>> | Unsigned right shift | 5 >>> 1 | 2 |
Ví dụ:
int a = 5; // 0101 in binary
int b = 3; // 0011 in binary
System.out.println(a & b); // 1 (0001)
System.out.println(a | b); // 7 (0111)
System.out.println(a ^ b); // 6 (0110)
System.out.println(~a); // -6 (invert bits)
System.out.println(a << 1); // 10 (shift left = multiply by 2)
System.out.println(a >> 1); // 2 (shift right = divide by 2)
Use cases:
// Check if even/odd (faster than %)
boolean isEven = (num & 1) == 0;
// Multiply/divide by powers of 2
int doubled = num << 1; // num * 2
int halved = num >> 1; // num / 2
// Flags (combine multiple boolean values)
int READ = 1; // 0001
int WRITE = 2; // 0010
int EXECUTE = 4; // 0100
int permissions = READ | WRITE; // 0011 (read + write)
boolean canRead = (permissions & READ) != 0; // true
7. Ternary Operator (Toán tử ba ngôi)
Syntax: condition ? valueIfTrue : valueIfFalse
int age = 20;
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // "Adult"
Equivalent to:
String status;
if (age >= 18) {
status = "Adult";
} else {
status = "Minor";
}
Ví dụ:
// Find max
int a = 10, b = 20;
int max = (a > b) ? a : b; // 20
// Absolute value
int num = -5;
int abs = (num >= 0) ? num : -num; // 5
// Nested ternary (không khuyến khích - khó đọc)
int score = 85;
String grade = (score >= 90) ? "A" : (score >= 80) ? "B" : "C";
Ternary Operator Type Rules
Ternary operator phải trả về cùng một type (hoặc compatible types).
Rule: Result type là common type của valueIfTrue và valueIfFalse.
Case 1: Same types — easy
int x = true ? 10 : 20; // Both int → result is int ✅
String s = false ? "A" : "B"; // Both String → result is String ✅
Case 2: Numeric types — promotes
int a = 10;
double b = 20.5;
double result = true ? a : b; // int vs double → promotes to double
System.out.println(result); // 10.0 (int promoted to double)
// Reverse:
double result2 = false ? a : b; // double
System.out.println(result2); // 20.5
Case 3: One branch is null — infer from non-null
String s = true ? "Hello" : null; // String vs null → String ✅
System.out.println(s); // "Hello"
String s2 = false ? null : "World"; // null vs String → String ✅
System.out.println(s2); // "World"
Case 4: Incompatible types — compile error!
// ❌ String vs int - incompatible
Object result = true ? "Hello" : 123; // Must use Object!
// ❌ Cannot mix unrelated types without common supertype
String s = true ? "Hello" : 123; // Compile error!
Case 5: Autoboxing/Unboxing
Integer x = 10;
int y = 20;
int result = true ? x : y; // Integer vs int → unbox Integer to int ✅
Integer result2 = false ? x : y; // int vs Integer → box int to Integer ✅
Case 6: null and primitives — NullPointerException!
Integer x = null;
int y = 20;
// ❌ Runtime NullPointerException!
int result = true ? x : y; // Tries to unbox null Integer → NPE!
Case 7: char and int
char c = 'A';
int n = 65;
int result = true ? c : n; // char vs int → int (char promoted)
System.out.println(result); // 65
char result2 = true ? c : n; // ❌ Compile error (int cannot narrow to char)
char result3 = (char) (true ? c : n); // ✅ Must cast
Case 8: Inheritance hierarchy
class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }
Animal pet = true ? new Dog() : new Cat(); // Dog vs Cat → Animal (common supertype) ✅
Case 9: Nested ternary type inference
int x = 5;
// (x > 10) ? "Big" : (x > 5) ? 100 : false
// String vs (int vs boolean) → No common type!
Object result = (x > 10) ? "Big" : (x > 5) ? 100 : false; // Must use Object
Dùng ternary khi:
- ✅ Simple, clear conditional assignment
int max = (a > b) ? a : b;
String status = isActive ? "Active" : "Inactive";
KHÔNG dùng ternary khi:
-
❌ Nested ternary (khó đọc)
// BAD
String grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "F";
// GOOD - Use if/else
String grade;
if (score >= 90) grade = "A";
else if (score >= 80) grade = "B";
else if (score >= 70) grade = "C";
else grade = "F"; -
❌ Complex expressions
// BAD
result = (a > 0 && b < 100) ? calculate(a, b) : performFallback(a, b);
// GOOD
if (a > 0 && b < 100) {
result = calculate(a, b);
} else {
result = performFallback(a, b);
} -
❌ Có side effects
// BAD - unclear order of execution
value = flag ? ++counter : --counter;
Operator Precedence (Thứ tự ưu tiên)
Khi có nhiều operators, Java thực thi theo thứ tự ưu tiên.
| Precedence | Operator | Mô tả |
|---|---|---|
| 1 (Highest) | () | Parentheses |
| 2 | ++, --, !, ~ | Unary |
| 3 | *, /, % | Multiplicative |
| 4 | +, - | Additive |
| 5 | <<, >>, >>> | Shift |
| 6 | <, <=, >, >= | Relational |
| 7 | ==, != | Equality |
| 8 | & | Bitwise AND |
| 9 | ^ | Bitwise XOR |
| 10 | | | Bitwise OR |
| 11 | && | Logical AND |
| 12 | || | Logical OR |
| 13 | ?: | Ternary |
| 14 (Lowest) | =, +=, -=, etc. | Assignment |
Ví dụ:
int result = 10 + 5 * 2; // 20 (not 30) - * has higher precedence
int result2 = (10 + 5) * 2; // 30 - parentheses override
boolean b = true || false && false; // true (&& before ||)
boolean b2 = (true || false) && false; // false - parentheses
int x = 5;
int y = x++ + ++x; // 5 + 7 = 12 (confusing! avoid this)
Trap 1: Arithmetic vs Comparison
int x = 5;
boolean result = x + 1 > 5; // (x + 1) > 5? or x + (1 > 5)?
// Answer: (x + 1) > 5 = 6 > 5 = true
// Arithmetic (+) có precedence cao hơn comparison (>)
Trap 2: Logical AND vs OR
boolean result = true || false && false; // true || (false && false)? or (true || false) && false?
// Answer: true || (false && false) = true || false = true
// && có precedence cao hơn ||
System.out.println(result); // true
Trap 3: Assignment vs Comparison
int x = 5;
if (x = 10) { } // ❌ Compile error! (x = 10 is int, not boolean)
boolean flag = false;
if (flag = true) { } // ✅ Compiles! (but bad practice - assigns true, then checks)
Trap 4: Increment trong expression
int x = 5;
int y = ++x + x++; // ++x = 6, then 6 + 6 = 12, then x++ → x = 7
System.out.println(y); // 12
System.out.println(x); // 7
// Rất khó đọc! Tránh dùng nhiều increments trong 1 expression
Trap 5: Bitwise vs Logical
boolean a = true;
boolean b = false;
// & has higher precedence than &&
boolean result = a && b & a; // a && (b & a)? or (a && b) & a?
// Answer: a && (b & a) = true && (false & true) = true && false = false
// Nhưng nếu không cẩn thận:
if (x > 0 && y > 0 & z > 0) { } // Evaluates (y > 0 & z > 0) first!
Trap 6: Ternary operator precedence
int x = 5;
int y = x > 3 ? 10 : 20 + 5; // (x > 3 ? 10 : 20) + 5? or x > 3 ? 10 : (20 + 5)?
// Answer: x > 3 ? 10 : (20 + 5) = 10 (ternary có precedence thấp)
System.out.println(y); // 10
Dùng parentheses để làm rõ intent, tránh nhầm lẫn:
// ❌ BAD - Unclear, dễ nhầm
if (a && b || c && d) { }
int result = a + b * c / d;
// ✅ GOOD - Clear intent
if ((a && b) || (c && d)) { }
int result = a + ((b * c) / d);
// Even if precedence is correct, parentheses improve readability!
Quy tắc an toàn:
- Dùng parentheses cho mọi complex expressions
- Đừng tin vào memory về precedence — make it explicit!
- Code được đọc nhiều hơn viết — optimize cho readability
Bài tập thực hành
Bài 1: Calculator với Operators
Viết chương trình nhận 2 số và phép toán từ bàn phím, in kết quả:
Expected output:
Nhập số thứ nhất: 10
Nhập phép toán (+, -, *, /, %): *
Nhập số thứ hai: 5
Kết quả: 10.00 * 5.00 = 50.00
Xem đáp án
import java.util.Scanner;
public class OperatorExercise {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Nhập số thứ nhất: ");
double num1 = sc.nextDouble();
System.out.print("Nhập phép toán (+, -, *, /, %): ");
char operator = sc.next().charAt(0);
System.out.print("Nhập số thứ hai: ");
double num2 = sc.nextDouble();
double result = 0;
boolean valid = true;
if (operator == '+') result = num1 + num2;
else if (operator == '-') result = num1 - num2;
else if (operator == '*') result = num1 * num2;
else if (operator == '/') result = num1 / num2;
else if (operator == '%') result = num1 % num2;
else valid = false;
if (valid) {
System.out.printf("Kết quả: %.2f %c %.2f = %.2f%n",
num1, operator, num2, result);
} else {
System.out.println("Phép toán không hợp lệ!");
}
sc.close();
}
}
Bài 2: Bitwise Permission Check
Viết chương trình sử dụng bitwise operators để quản lý permissions:
Xem đáp án
public class PermissionCheck {
static final int READ = 1; // 0001
static final int WRITE = 2; // 0010
static final int EXECUTE = 4; // 0100
public static void main(String[] args) {
int userPermission = READ | WRITE; // 0011
System.out.println("Can read: " + ((userPermission & READ) != 0)); // true
System.out.println("Can write: " + ((userPermission & WRITE) != 0)); // true
System.out.println("Can execute: " + ((userPermission & EXECUTE) != 0)); // false
// Add execute permission
userPermission |= EXECUTE; // 0111
System.out.println("After adding execute: " + ((userPermission & EXECUTE) != 0)); // true
// Remove write permission
userPermission &= ~WRITE; // 0101
System.out.println("After removing write: " + ((userPermission & WRITE) != 0)); // false
}
}
Tổng kết
Trong bài này, bạn đã học:
✅ Arithmetic: +, -, *, /, %
✅ Unary: ++, --, !
✅ Assignment: =, +=, -=, *=, /=, %=
✅ Comparison: ==, !=, >, <, >=, <=
✅ Logical: && (short-circuit), || (short-circuit), !
✅ Bitwise: &, |, ^, ~, <<, >>
✅ Ternary: condition ? value1 : value2
✅ Operator Precedence: Parentheses > Unary > Arithmetic > Comparison > Logical > Assignment
Bài tiếp theo chúng ta sẽ học về Luồng điều khiển (Control Flow) - if/else, switch, loops!