Toán tử dùng để làm gì
Toán tử (operator) là các ký hiệu kết hợp các giá trị để tính ra giá trị mới. Bạn vốn đã dùng chúng một cách tự nhiên: + để cộng, > để so sánh. Java chia chúng thành vài nhóm: số học, so sánh, logic, gán, và một số toán tử đặc biệt như tăng giảm và toán tử ba ngôi.
Phần lớn toán tử nhận hai giá trị (một đứng trước, một đứng sau ký hiệu) và tạo ra một kết quả. Mấu chốt là biết kết quả đó có kiểu gì và các toán tử được áp dụng theo thứ tự nào, đó cũng là chỗ người mới hay vấp.
Toán tử số học
Năm toán tử số học làm việc với các con số: +, -, *, / và % (chia lấy dư).
Toán tử % cho phần dư sau phép chia: 17 % 5 là 2 vì 17 chia 5 được 3 lần và còn dư 2. Đây là công cụ quen thuộc để kiểm tra tính chia hết: n % 2 == 0 là cách chuẩn để hỏi "n có chẵn không?".
Cũng lưu ý rằng + làm hai việc: với số thì nó cộng, nhưng khi một trong hai bên là String thì nó nối chuỗi.
Dòng đầu tiên được đọc từ trái sang phải: "Total: " + 3 đã là một chuỗi, nên 4 được nối vào dưới dạng văn bản. Hãy bọc phép tính trong ngoặc khi muốn nó được tính trước.
Cái bẫy phép chia nguyên
Đây là điều gây bất ngờ nhiều nhất. Khi cả hai toán hạng của / đều là số nguyên, Java thực hiện phép chia nguyên và bỏ phần thập phân: nó không làm tròn mà cắt bỏ về phía số 0.
Để có kết quả thập phân, hãy cho ít nhất một toán hạng là double: hoặc viết nó kèm .0, hoặc ép kiểu một bên bằng (double). Ép kiểu (double)(5 / 2) không giúp được gì, vì phép chia nguyên 5 / 2 đã xảy ra trước khi ép kiểu; bạn phải ép kiểu một toán hạng, không phải kết quả.
Toán tử so sánh
Toán tử so sánh đặt một câu hỏi có hoặc không và trả về một boolean. Có sáu toán tử: ==, !=, <, >, <=, >=.
Hãy để ý sự khác nhau giữa = (phép gán, "đặt giá trị này vào đây") và == (phép so sánh, "hai bên có bằng nhau không?"). Nhầm hai thứ này là một bug kinh điển.
Một lưu ý quan trọng: == so sánh tham chiếu của đối tượng, không so sánh nội dung. Với chuỗi, hãy dùng .equals().
Dùng == cho các kiểu nguyên thủy như int và boolean, và dùng .equals() mỗi khi so sánh nội dung của đối tượng, chẳng hạn chuỗi.
Toán tử logic
Toán tử logic kết hợp các giá trị boolean: && (và), || (hoặc) và ! (phủ định). Đây là cách bạn diễn đạt những điều kiện như "đủ tuổi và có vé".
&& và || có tính đoản mạch (short-circuit): Java dừng tính toán ngay khi đã chắc chắn về kết quả. Với &&, nếu vế trái là false thì vế phải không bao giờ được kiểm tra; với ||, nếu vế trái là true thì vế phải bị bỏ qua. Đây không chỉ là chuyện tối ưu, bạn còn có thể dựa vào nó để code an toàn hơn:
if (text != null && text.length() > 0) { ... }
Nếu text là null, vế trái là false và text.length() không bao giờ được gọi, nhờ vậy tránh được NullPointerException. Đảo ngược thứ tự là chương trình sẽ lỗi.
Gán và tăng giảm
= dùng để gán, nhưng Java còn có các phép gán kết hợp, gộp một phép toán với phép gán: +=, -=, *=, /=, %=. Còn ++ và -- thì cộng thêm hoặc trừ đi một.
++ có hai dạng: hậu tố (count++) trả về giá trị cũ rồi mới tăng; tiền tố (++count) tăng trước rồi trả về giá trị mới. Sự khác biệt chỉ có ý nghĩa khi bạn dùng kết quả ngay trong cùng một biểu thức:
Khi ++ đứng riêng trên một dòng, hậu tố hay tiền tố đều như nhau, nên hãy chọn cách nào dễ đọc và đừng nhồi phép tăng vào những biểu thức lớn hơn.
Toán tử ba ngôi
Toán tử ba ngôi condition ? a : b là một if/else gọn nhẹ tạo ra một giá trị: nếu điều kiện là true thì nó cho ra a, ngược lại cho ra b.
Nó rất hợp với các phép gán ngắn kiểu chọn một trong hai. Đừng lồng toán tử ba ngôi vào trong nhau: code sẽ nhanh chóng khó đọc, và một if/else bình thường rõ ràng hơn.
Độ ưu tiên và dấu ngoặc
Các toán tử được áp dụng theo một thứ tự cố định, giống như trong toán học: *, /, % có độ ưu tiên cao hơn + và -, hai toán tử này cao hơn các phép so sánh, các phép so sánh lại cao hơn &&, rồi đến ||.
Bạn không cần thuộc lòng cả bảng. Khi một biểu thức trộn nhiều nhóm toán tử hoặc bạn phải dừng lại để nghĩ xem thứ tự thế nào, hãy thêm dấu ngoặc: chúng chẳng tốn gì mà giúp người đọc sau hiểu ngay ý bạn.
Tiếp theo: ép kiểu
Nhiều cái bẫy ở đây, như phép chia nguyên hay trộn int với double, đều quy về chuyện kiểu dữ liệu. Chủ động chuyển đổi giữa các kiểu số được gọi là ép kiểu (casting), và đó là nội dung của trang tiếp theo.
Câu hỏi thường gặp
Toán tử % trong Java dùng để làm gì?
% là toán tử chia lấy dư (modulo): nó cho phần còn lại sau phép chia nguyên. 7 % 3 là 1, và 10 % 2 là 0. Đây là cách chuẩn để kiểm tra tính chia hết (n % 2 == 0 nghĩa là n chẵn) và để giữ một giá trị quay vòng trong một khoảng.
Vì sao 5 / 2 trong Java cho ra 2 chứ không phải 2.5?
Khi cả hai toán hạng đều là số nguyên, / thực hiện phép chia nguyên và bỏ phần thập phân, nên 5 / 2 là 2, không phải 2.5. Để có 2.5, cần ít nhất một toán hạng là double: viết 5.0 / 2, 5 / 2.0, hoặc ép kiểu một bên với (double) 5 / 2.
== và equals() trong Java khác nhau thế nào?
== so sánh kiểu nguyên thủy theo giá trị, nhưng với đối tượng (kể cả String) nó so sánh tham chiếu, tức hai bên có phải cùng một đối tượng trong bộ nhớ hay không. Để so sánh nội dung của đối tượng, hãy dùng .equals(). So sánh hai chuỗi bằng == có thể trả về false dù nội dung giống nhau, nên với chuỗi hãy luôn dùng "a".equals(b).