杰瑞科技汇

Java字符串比较用==还是equals?

String 类的比较主要有三种方式,它们各有用途,绝不能混为一谈:

Java字符串比较用==还是equals?-图1
(图片来源网络,侵删)
  1. 运算符:比较引用地址(是否是同一个对象)。
  2. equals() 方法:比较(字符序列是否完全相同)。
  3. compareTo() 方法:比较字符串字典序(按 Unicode 码值逐个字符比较)。

下面我们逐一深入讲解。


运算符

是 Java 中的一个关系运算符,用于比较两个变量所引用的对象内存地址是否相同。

核心要点:

  • 比较的是“地址”,不是“内容”
  • 如果两个 String 变量指向内存中的同一个 String 对象(即同一个地址),则 返回 true
  • 如果它们指向不同的对象,即使内容完全一样, 也返回 false

两种情况分析:

字面量赋值(String Pool / 字符串常量池)

当使用双引号 创建一个字符串时,JVM 会首先在字符串常量池中查找是否存在相同内容的字符串,如果存在,就直接引用该对象;如果不存在,则创建一个新的字符串对象并存入池中。

Java字符串比较用==还是equals?-图2
(图片来源网络,侵删)
String s1 = "hello";
String s2 = "hello";
// s1 和 s2 都指向常量池中同一个 "hello" 对象
System.out.println(s1 == s2); // 输出: true
String s3 = "world";
String s4 = new String("world");
// s3 指向常量池中的 "world"
// s4 指向堆中 newly created 的 "world" 对象
System.out.println(s3 == s4); // 输出: false

new 关键字创建

使用 new 关键字创建字符串时,JVM 不会在字符串常量池中查找,而是直接在堆内存中创建一个新的 String 对象,即使内容已经存在。

String s5 = new String("java");
String s6 = new String("java");
// s5 和 s6 都是在堆中新创建的对象,地址不同
System.out.println(s5 == s6); // 输出: false

equals() 方法

equals()Object 类的方法,String 类对其进行了重写,专门用于比较两个字符串的是否完全一致。

核心要点:

  • 比较的是“内容”,不是“地址”
  • 它会逐个字符比较,只有当所有字符都相同时,才返回 true
  • 这是判断两个字符串内容是否相等的标准方法
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
// s1 和 s2 内容相同
System.out.println(s1.equals(s2)); // 输出: true
// s1 和 s3 内容也相同,尽管地址不同
System.out.println(s1.equals(s3)); // 输出: true
// 注意:为了避免空指针异常,推荐使用 "常量.equals(变量)" 的形式
String s4 = null;
// System.out.println(s4.equals("hello")); // 这会抛出 NullPointerException
System.out.println("hello".equals(s4)); // 输出: false,更安全
// Java 7+ 引入了 Objects.equals(),可以更优雅地处理 null
// import java.util.Objects;
// System.out.println(Objects.equals(s4, "hello")); // 输出: false

compareTo() 方法

compareTo() 方法用于按字典序(lexicographical order)比较两个字符串,它基于字符的 Unicode 码值进行比较。

Java字符串比较用==还是equals?-图3
(图片来源网络,侵删)

核心要点:

  • 按字典序比较
  • 返回一个整型值,这个值的意义是:
    • 返回 0:两个字符串内容完全相等。
    • 返回负整数:调用此方法的字符串小于参数字符串。
    • 返回正整数:调用此方法的字符串大于参数字符串。

比较规则:

  1. 从左到右,逐个字符比较其 Unicode 码值。
  2. 在第一个不相等的字符处,比较结束,返回这两个字符码值的差值。
  3. 如果所有字符都相等,但一个字符串比另一个长,则返回长度差值。
  4. 如果所有字符都相等且长度也相等,则返回 0
String s1 = "apple";
String s2 = "banana";
String s3 = "apple";
String s4 = "applet";
// 'a' 的 Unicode 码值小于 'b',"apple" < "banana"
System.out.println(s1.compareTo(s2)); // 输出一个负数 ( -1)
完全相同
System.out.println(s1.compareTo(s3)); // 输出: 0
// "apple" 和 "applet" 前5个字符相同,但 "applet" 更长
System.out.println(s1.compareTo(s4)); // 输出一个负数 ( -1, 因为 't' 的码值)
System.out.println(s4.compareTo(s1)); // 输出一个正数 ( 1)
// 大小写敏感:'A' (65) 和 'a' (97)
String s5 = "Apple";
System.out.println(s1.compareTo(s5)); // 输出一个正数 (97 - 65 = 32)

不区分大小写的比较:compareToIgnoreCase()

如果需要不区分大小写的字典序比较,可以使用 compareToIgnoreCase() 方法。

String s1 = "Apple";
String s2 = "apple";
System.out.println(s1.compareTo(s2)); // 输出: -32 (因为 'A' < 'a')
System.out.println(s1.compareToIgnoreCase(s2)); // 输出: 0

总结与最佳实践

方法 用途 示例 结果
判断是否为同一个对象 引用地址 "a" == new String("a") false
equals() 是否相等 字符序列 "a".equals(new String("a")) true
compareTo() 按字典序比较大小 字符序列的 Unicode 值 "b".compareTo("a") 正数

何时使用哪个?

  1. 判断是否为同一个对象实例

    • 在某些性能敏感的场景下,或者当你明确知道需要检查对象唯一性时,使用 。
    • 示例:缓存系统中,检查缓存中是否已存在某个对象。
      if (cache.get(key) == value) {
        // 缓命中,且是同一个对象
      }
  2. 判断字符串内容是否相等(最常用)

    • 99% 的情况下,当你想比较两个字符串是否“一样”时,都应该使用 equals()
    • 示例:用户登录、密码校验、配置项比对等。
      if (username.equals("admin") && password.equals("123456")) {
        // ...
      }
    • 安全写法:为了避免 NullPointerException,推荐将常量放在前面。
      if ("admin".equals(username) && "123456".equals(password)) {
        // ...
      }
  3. 需要比较字符串大小顺序时

    • 当你需要对字符串进行排序(如按字母顺序、日期顺序等)时,使用 compareTo()
    • 示例:对一组单词进行字典序排序。
      String[] words = {"banana", "apple", "cherry"};
      Arrays.sort(words); // 内部会使用 compareTo() 方法进行比较

常见误区

误区: 和 equals 没什么区别,哪个用着方便就用哪个。

真相:这是初学者最容易犯的错误,混淆这两者会导致难以发现的 bug,在登录功能中,如果误用 来比较用户输入的密码和数据库中存储的密码(即使内容相同,也可能是不同对象),会导致登录永远失败。

记住这个黄金法则

  • 想知道是否一样?用 equals()
  • 想知道是不是同一个东西(同一个内存地址)?用 。
分享:
扫描分享到社交APP
上一篇
下一篇