你必须分清楚两种“相等”的含义:
- 内容相等 (值相等):两个字符串对象包含的字符序列完全相同。
"hello"和"hello"。 - 引用相等 (对象相等):两个变量指向内存中的同一个
String对象。String s1 = new String("abc"); String s2 = s1;。
下面我们详细讲解如何判断这两种情况,以及为什么。
相等 (最常用的情况)
这是绝大多数情况下你真正想要做的判断,判断两个字符串内容是否相同,必须使用 equals() 方法。
equals() 方法
equals() 方法是 Object 类定义的方法,String 类对其进行了重写,使其比较的是字符串的内容(字符序列)。
语法:
str1.equals(str2);
str1 和 str2 的内容完全相同,则返回 true;否则返回 false。
示例:
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = "world";
相同,返回 true
System.out.println(s1.equals(s2)); // 输出: true
不同,返回 false
System.out.println(s1.equals(s3)); // 输出: false
equalsIgnoreCase() 方法
这是一个非常实用的变体,它在比较时忽略大小写。
语法:
str1.equalsIgnoreCase(str2);
示例:
String s1 = "Hello"; String s2 = "hello"; String s3 = "HELLO"; System.out.println(s1.equals(s2)); // 输出: false (区分大小写) System.out.println(s1.equalsIgnoreCase(s2)); // 输出: true (忽略大小写) System.out.println(s1.equalsIgnoreCase(s3)); // 输出: true
判断引用相等 (较少使用)
判断两个变量是否指向内存中的同一个对象,应该使用 运算符。
比较的是两个变量在栈中存储的内存地址(引用)是否相同。
语法:
str1 == str2;
str1 和 str2 指向同一个 String 对象,则返回 true;否则返回 false。
示例:
// 情况1:通过 new 关键字创建
String s1 = new String("abc");
String s2 = new String("abc");
// s1 和 s2 是两个不同的对象,内存地址不同
System.out.println(s1 == s2); // 输出: false
// 情况2:通过字面量赋值 (Java 特有的字符串常量池机制)
String s3 = "abc";
String s4 = "abc";
// Java 会对字面量 "abc" 进行优化,只创建一个对象,s3 和 s4 都指向这个对象
System.out.println(s3 == s4); // 输出: true
// 情况3:混合情况
String s5 = "abc";
String s6 = new String("abc");
System.out.println(s5 == s6); // 输出: false,因为 s6 是 new 出来的新对象
为什么 在判断字符串内容时是危险的?
因为 比较的是内存地址,而不是内容,在开发中,我们几乎总是关心内容是否相同,而不是它们是不是同一个对象,如果你误用了 ,可能会导致逻辑错误,尤其是在处理用户输入、从文件或网络读取数据时。
反例:
// 假设这是从用户输入获取的字符串
String userInput = new Scanner(System.in).nextLine();
String password = "secret123";
// 错误的判断方式!
if (userInput == password) {
System.out.println("密码正确!");
} else {
System.out.println("密码错误!");
}
// 即使你输入 "secret123",这个判断也几乎永远是 false,
// 因为 userInput 是 new 出来的新对象,与 password 不是同一个引用。
最佳实践与性能考量
使用 equals() 时的 NullPointerException 风险
如果你尝试在一个 null 对象上调用 equals() 方法,程序会抛出 NullPointerException。
String s1 = null; String s2 = "hello"; // 这行代码会抛出 NullPointerException System.out.println(s1.equals(s2));
解决方案:
-
反向调用(推荐):将常量或非
null的对象放在equals()的左侧。// s1 是 null,表达式会变成 "hello".equals(s1),返回 false,不会报错 System.out.println("hello".equals(s1)); -
使用
Objects.equals()(Java 7+):这是最安全、最推荐的方式。java.util.Objects类提供了一个静态的equals方法,它内部已经处理了null的情况。import java.util.Objects; String s1 = null; String s2 = "hello"; // 安全,不会抛出异常 System.out.println(Objects.equals(s1, s2)); // 输出: false
性能:equals() vs
通常情况下, 的性能比 equals() 要快,因为 只是直接比较两个内存地址(一个原生数据类型比较),而 equals() 需要先检查类型,然后逐个字符进行比较。
性能差异只有在比较两个已知非 null 的对象时才有意义。
在编写业务代码时,可读性和正确性远比微小的性能差异重要,99% 的情况下,你应该使用 equals() 来判断字符串内容。
总结表格
| 方法/运算符 | 是否可为 null |
示例 | 推荐场景 | |
|---|---|---|---|---|
equals() |
(区分大小写) | 不能为 null (调用方) |
"abc".equals(str) |
判断字符串内容是否相等(默认) |
equalsIgnoreCase() |
(忽略大小写) | 不能为 null (调用方) |
"ABC".equalsIgnoreCase(str) |
判断字符串内容是否相等,且不关心大小写(如用户名、密码) |
Objects.equals() |
(区分大小写) | 可以为 null (任一参数) |
Objects.equals(str1, str2) |
最安全、最推荐的通用方法,能完美处理 null |
| 内存地址 (对象引用) | 可以 | str1 == str2 |
判断两个变量是否指向同一个对象,不用于判断内容 |
黄金法则
当你想判断两个
String的内容是否相同时,请使用equals(),为了代码的健壮性,优先使用Objects.equals(str1, str2)。
