So sánh chuỗi với equals và toán tử ==
Vấn đề
Phần tiêu đề “Vấn đề”== trên đối tượng (bao gồm String) so sánh tham chiếu (địa chỉ trong bộ nhớ), không so sánh nội dung. Điều này thường gây bất ngờ vì String “trông giống” kiểu nguyên thủy:
String a = "hello";String b = "hello";System.out.println(a == b); // true - NHƯNG chỉ vì cả hai literal trùng nhau trong string pool!
String c = new String("hello");System.out.println(a == c); // false - c là một đối tượng MỚI, khác tham chiếu với a!System.out.println(a.equals(c)); // true - equals() so sánh đúng NỘI DUNGVì sao nguy hiểm?
Phần tiêu đề “Vì sao nguy hiểm?”Kết quả của == trên String literal có thể tình cờ đúng nhờ cơ chế string pool (Java tái sử dụng các literal giống nhau), khiến lập trình viên lầm tưởng == luôn hoạt động đúng cho String - cho đến khi gặp chuỗi được tạo bằng new String(...), đọc từ file, hoặc ghép từ nhiều phần (những trường hợp không dùng string pool), thì == bất ngờ trả về false dù nội dung giống hệt:
String input = new Scanner(System.in).nextLine(); // giả sử người dùng nhập "yes"if (input == "yes") { // BẪY! Gần như luôn false, dù input thực sự là "yes" System.out.println("Đồng ý");}Giải pháp
Phần tiêu đề “Giải pháp”Luôn dùng .equals() (hoặc .equalsIgnoreCase()) để so sánh nội dung chuỗi, không bao giờ dùng ==:
String input = new Scanner(System.in).nextLine();if (input.equals("yes")) { // ĐÚNG cách System.out.println("Đồng ý");}⚠️ Nếu input có thể là null, gọi input.equals("yes") sẽ ném NullPointerException. Cách an toàn hơn: đặt literal (chắc chắn không null) làm bên gọi equals():
if ("yes".equals(input)) { // an toàn kể cả khi input là null -> trả về false, không ném lỗi System.out.println("Đồng ý");}Nguyên tắc tương tự áp dụng cho mọi kiểu đối tượng khác (Integer, các class tự định nghĩa,…) - == luôn so sánh tham chiếu trừ khi là kiểu nguyên thủy.
Tóm tắt
Phần tiêu đề “Tóm tắt”
==trên String so sánh tham chiếu, không so sánh nội dung - kết quả có thể “tình cờ đúng” nhờ string pool, gây hiểu nhầm.
- Luôn dùng
.equals()/.equalsIgnoreCase()để so sánh nội dung chuỗi- Đặt literal ở bên trái lời gọi
equals()("yes".equals(x)) để tránhNullPointerExceptionkhi biến có thể lànull