杰瑞科技汇

Java中字符串equals方法怎么用?

核心要点

  1. vs equals(): 这是 Java 中最常见的混淆点。

    • 比较的是变量中存储的值(内存地址)。
    • equals():比较的是(对于字符串来说,就是字符序列)。
  2. String 类的 equals() 方法String 类重写了 Object 类的 equals() 方法,专门用于比较两个字符串对象的内容是否完全相同(区分大小写)。

  3. equalsIgnoreCase() 方法:当你需要比较字符串内容是否相同,但不关心大小写时,应该使用这个方法。


运算符:比较内存地址

在 Java 中,所有对象变量都是引用类型,它们存储的不是对象本身,而是对象在堆内存中的地址

让我们来看几个例子:

String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = str3;
System.out.println("str1 == str2: " + (str1 == str2)); // true
System.out.println("str1 == str3: " + (str1 == str3)); // false
System.out.println("str3 == str4: " + (str3 == str4)); // true

解释:

  • str1 == str2true:这是因为 Java 为了优化性能,会对字符串字面量("hello")进行字符串驻留(String Interning),当编译器遇到 "hello" 时,它会在字符串常量池中查找是否存在这个字符串,如果存在,就直接引用;如果不存在,就创建一个新的并放入池中。str1str2 都指向了常量池中同一个 "hello" 对象,所以它们的内存地址(引用)是相同的。

  • str1 == str3falsenew String("hello") 的含义是“强制在堆内存中创建一个新的字符串对象”,这个对象的内容是 "hello",但它与常量池中的 "hello" 是两个完全不同的对象,拥有不同的内存地址。str1(指向常量池)和 str3(指向堆内存)的引用地址不同。

  • str3 == str4truestr4 被直接赋值为 str3,这意味着 str4str3 指向了堆内存中同一个 String 对象,它们的引用地址自然相同。

如果你只想判断两个字符串变量是否指向同一个对象(同一个内存地址),用 ,但在绝大多数业务场景下,我们关心的是字符串内容是否一样,而不是它们是不是同一个对象。


equals() 方法:比较字符串内容

String 类重写了 Objectequals() 方法,使其专门用于比较字符串的字符序列是否完全一致。

String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println("str1.equals(str2): " + str1.equals(str2)); // true
System.out.println("str1.equals(str3): " + str1.equals(str3)); // true

解释:

  • str1.equals(str2)true:虽然 str1str2 是同一个对象(str1 == str2 也为 true),但 equals() 方法会逐个比较它们的字符,因为内容都是 "hello",所以返回 true
  • str1.equals(str3)truestr1str3 是不同的对象(str1 == str3false),但 equals() 方法不关心地址,只关心内容,因为它们的内容都是 "hello",所以返回 true

equals() 方法的实现逻辑(简化版)

// 这是 Object 类中 equals() 的默认实现,比较地址
public boolean equals(Object obj) {
    return (this == obj);
}
// String 类重写后的实现
public boolean equals(Object anObject) {
    // 1. 先判断地址是否相同,如果相同,直接返回 true,提高效率
    if (this == anObject) {
        return true;
    }
    // 2. 判断参数是否是 String 类型的实例
    if (anObject instanceof String) {
        // 3. 强制转换为 String 类型
        String anotherString = (String)anObject;
        // 4. 比较两个字符串的长度,如果不同,直接返回 false
        int n = value.length;
        if (n == anotherString.value.length) {
            // 5. 逐个字符进行比较
            char v1[] = value;
            char v2[] = anotherString.value;
            for (int i = 0; i < n; i++) {
                if (v1[i] != v2[i]) {
                    return false; // 只要有一个字符不同,就返回 false
                }
            }
            return true; // 所有字符都相同,返回 true
        }
    }
    return false;
}

equalsIgnoreCase() 方法:忽略大小写比较

当你需要比较字符串,但希望 "Hello" 和 "hello" 被认为是相同时,equalsIgnoreCase() 就派上用场了。

String s1 = "Hello World";
String s2 = "hello world";
String s3 = "HELLO WORLD";
System.out.println(s1.equals(s2));      // false,因为 'H' != 'h'
System.out.println(s1.equalsIgnoreCase(s2)); // true,忽略大小写后内容相同
System.out.println(s1.equalsIgnoreCase(s3)); // true

equals()equalsIgnoreCase() 的最佳实践

永远不要使用 来比较字符串的内容

这是 Java 开发中的一条黄金法则,除非你有非常特殊的需求(检查单例模式中是否返回了同一个实例),否则请坚持使用 equals()equalsIgnoreCase()

避免 NullPointerException (NPE)

如果你尝试在一个 null 对象上调用 equals() 方法,程序会抛出 NullPointerException

String s = null;
String t = "hello";
// s.equals(t); // 这行代码会抛出 NullPointerException

安全的写法:

为了避免 NPE,有两种常见的最佳实践:

  1. 将常量放在前面(推荐): 这是业界最推荐的做法,因为常量("hello")肯定不是 nullequals() 方法永远不会被 null 对象调用。

    String s = null;
    boolean isHello = "hello".equals(s); // 返回 false,不会抛出异常
  2. 使用 Objects.equals() (Java 7+)java.util.Objects 工具类提供了一个静态的 equals() 方法,它内部已经处理了 null 检查,非常安全。

    import java.util.Objects;
    String s1 = null;
    String s2 = "hello";
    String s3 = "hello";
    System.out.println(Objects.equals(s1, s2)); // false,安全
    System.out.println(Objects.equals(s2, s3)); // true
    // Objects.equals 内部逻辑类似于:
    //   return (a == b) || (a != null && a.equals(b));

总结表格

方法 区分大小写 示例 ("Hello".equals(...)) 空指针安全 (null.equals(...))
内存地址(引用) N/A "a" == new String("a") -> false NullPointerException
equals() (字符序列) "Hello".equals("hello") -> false NullPointerException
equalsIgnoreCase() (字符序列) "Hello".equalsIgnoreCase("hello") -> true NullPointerException
Objects.equals() (字符序列) Objects.equals("Hello", "hello") -> false 安全,返回 false

最佳实践代码示例

import java.util.Objects;
public class StringComparison {
    public static void main(String[] args) {
        String username = null; // 模拟从数据库或配置中获取的用户名
        String inputUsername = "admin";
        // 错误示范:会抛出 NullPointerException
        // if (username.equals(inputUsername)) { ... }
        // 正确示范 1:将常量放在前面
        if ("admin".equals(username)) {
            System.out.println("Welcome, admin!");
        } else {
            System.out.println("Unknown user.");
        }
        // 正确示范 2:使用 Objects.equals() (更通用)
        if (Objects.equals(username, inputUsername)) {
            System.out.println("Welcome, " + inputUsername + "!");
        } else {
            System.out.println("Authentication failed.");
        }
        // 忽略大小写比较
        String systemName = "LINUX";
        String userInput = "linux";
        if (systemName.equalsIgnoreCase(userInput)) {
            System.out.println("System recognized.");
        }
    }
}
分享:
扫描分享到社交APP
上一篇
下一篇