Chuyển tới nội dung chính

Toán tử (Operators)

Mục tiêu bài học

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)

OperatorTênVí dụKết quả
+Addition (Cộng)5 + 38
-Subtraction (Trừ)5 - 32
*Multiplication (Nhân)5 * 315
/Division (Chia)5 / 22 (integer division)
%Modulus (Chia lấy dư)5 % 21

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)
Integer Division

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)

OperatorTênVí dụKết quả
+Unary plus+55
-Unary minus (Đổi dấu)-5-5
++Increment (Tăng 1)x++ hoặc ++xTăng x lên 1
--Decrement (Giảm 1)x-- hoặc --xGiảm x xuống 1
!Logical NOT!truefalse

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
Best Practice

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)

OperatorTương đươngVí dụKết quả
=Gánx = 5x = 5
+=x = x + yx += 3x = x + 3
-=x = x - yx -= 3x = x - 3
*=x = x * yx *= 3x = x * 3
/=x = x / yx /= 3x = x / 3
%=x = x % yx %= 3x = 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).

OperatorTênVí dụKết quả
==Equal to5 == 5true
!=Not equal5 != 3true
>Greater than5 > 3true
<Less than5 < 3false
>=Greater than or equal5 >= 5true
<=Less than or equal5 <= 3false

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
So sánh String

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.

OperatorTênMô tảVí dụKết quả
&&ANDCả 2 đều truetrue && falsefalse
||ORÍt nhất 1 truetrue || falsetrue
!NOTĐảo ngược!truefalse

Truth tables:

AND (&&):

ABA && B
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse

OR (||):

ABA || B
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse

NOT (!):

A!A
truefalse
falsetrue

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:

&&||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:

OperatorNameShort-circuit?Use case
&&Logical AND✅ YesMost common — safe, efficient
&Bitwise AND❌ NoBitwise operations, hoặc cần evaluate cả 2 sides
||Logical OR✅ YesMost common — safe, efficient
|Bitwise OR❌ NoBitwise 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ì &&, ||?

  1. 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
  1. Bitwise operations:
int flags = 0b1010;
int mask = 0b0110;
int result = flags & mask; // 0b0010 (bitwise AND)
🔥 Bẫy OCP: Short-circuit edge cases

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!)
💡 Cách nhớ: && và || như "lazy evaluation"
  • &&: "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.

OperatorTênVí dụKết quả
&Bitwise AND5 & 31 (0101 & 0011 = 0001)
|Bitwise OR5 | 37 (0101 | 0011 = 0111)
^Bitwise XOR5 ^ 36 (0101 ^ 0011 = 0110)
~Bitwise NOT~5-6 (invert bits)
<<Left shift5 << 110 (0101 << 1 = 1010)
>>Right shift5 >> 12 (0101 >> 1 = 0010)
>>>Unsigned right shift5 >>> 12

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

🔥 Bẫy OCP: Ternary type inference

Ternary operator phải trả về cùng một type (hoặc compatible types).

Rule: Result type là common type của valueIfTruevalueIfFalse.

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
Best Practice: Ternary operator guidelines

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.

PrecedenceOperatorMô 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)
🔥 Bẫy OCP: Operator precedence traps

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
Best Practice: Dùng parentheses liberally!

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 : value2Operator Precedence: Parentheses > Unary > Arithmetic > Comparison > Logical > Assignment

Bước tiếp theo

Bài tiếp theo chúng ta sẽ học về Luồng điều khiển (Control Flow) - if/else, switch, loops!

👉 Tiếp theo: Luồng điều khiển →

Đọc thêm