深入浅出 Java 字符串比较

你是否曾想过 Java 中的 String 方法、关键字和操作符是如何在字符串池中进行比较操作的?本文就带你了解了解!

Java 的 String 类封装了一个字节数组。这个字节数组可以转换为字符,在这种情况下,String 就变成了用来组成单词、句子或任何其他你想要的数据的字符数组。

在本文中,我们将介绍如何比较两个字符串是否相等。但是在开始之前,我会先介绍一些相关的概念。

String 类

当查看 Java中的 String 类时,可以看到字符数组是这样被封装的:

public String(char value[]) {
    this(value, 0, value.length, null);
}

方法重载和 String 类

方法重载是 String 类广泛使用的一种技术。重载可以使你的类非常灵活:

public String(String original) {}
public String(char value[], int offset, int count) {}
public String(int[] codePoints, int offset, int count) {}
public String(byte bytes[], int offset, int length, String charsetName) {}

字符串池

String 可能是 Java 中使用最频繁的类。如果我们每次使用 String 时都在内存堆中创建一个新对象,那么将会浪费大量的内存。字符串池通过为每个字符串值只存储一个对象来解决这个问题,如下所示:
请添加图片描述

尽管我们为"Duke"和"Juggy"这两个字符串分别创建了一个String变量,但在内存堆中只创建并存储了两个对象。为了证明这一点,请查看以下代码。)

String juggy = "Juggy";
String anotherJuggy = "Juggy";
System.out.println(juggy == anotherJuggy);

这段代码将返回 true,因为这两个字符串在字符串池中指向同一个对象, 它们的值是相同的。

new 操作符

这段代码看起来与之前的例子相似,但它有一个不同之处:

String duke = new String("duke");
String anotherDuke = new String("duke");

System.out.println(duke == anotherDuke);

基于前面的例子,你可能会期望这段代码返回 true,但实际上它返回的是 false。添加new 操作符会强制在内存堆中创建一个新的String对象。因此,JVM 将会创建两个不同的对象。

intern()

为了将字符串存储在字符串池中,我们使用了一种称为字符串驻留(String interning)的技术。以下是 Java Doc 关于intern()方法的描述:



    /**
     * Returns a canonical representation for the string object.
     *
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     *
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     *
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * 
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * The Java™ Language Specification.
     *
     * @returns  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     * @jls 3.10.5 String Literals
     */ public native String intern();

intern() 方法用于将字符串存储在字符串池中。首先,它会验证你创建的字符串是否已经存在于池中。如果不存在,它会在池中创建一个新的字符串。字符串池的逻辑是基于享元模式(Flyweight pattern)。

现在,请注意当我们使用 new 关键字强制创建两个字符串时会发生什么:

String duke = new String("duke");
String duke2 = new String("duke");
System.out.println(duke == duke2); // false 
System.out.println(duke.intern() == duke2.intern()); // true

与前面使用 new 关键字的例子不同,在这个例子中比较结果是 true。这是因为使用intern() 方法确保了字符串将被存储在字符串池中

使用 String 类的 equals() 方法

我们使用 equals() 方法来验证两个 Java 类的状态是否相同。因为 equals() 方法来自 Object 类,所以每个 Java 类都继承了这个方法。但是为了使 equals() 方法正常工作,必须对其进行重写。以下代码给出了如何使用equals()方法进行字符串比较:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
          return isLatin1() ? StringLatin1.equals(value, aString.value)
            : StringUTF16.equals(value, aString.value);
        }
    }
    
    return false;
}

如你所见,必须比较 String 类值的状态是否相等,而不是对象引用。即使对象引用不同也没关系,将会比较字符串的状态。

使用 String 时的常见错误

要判断两个字符串是否指向同一个对象可能会很困难,特别是当这两个字符串包含相同的值时。不过请记住,使用保留关键字 new 总是会在内存中创建一个新对象,即使它们的值相同,这一点对你理解这个问题绘很有帮助。

使用 String 方法来比较对象引用也可能很棘手。关键是,如果该方法改变了字符串中的某些内容,那么对象引用将会不同。

例如这个例子:

System.out.println("duke".trim() == "duke".trim());; 

这个比较将会是true,因为trim()方法不会生成一个新的字符串。

那么,再换一个写法:

System.out.println(" duke".trim() == "duke".trim()); 

在这个情况下,第一个trim()方法会生成一个新的字符串,因为该方法会执行其操作,所以引用将会不同。

最后,当trim()执行其操作时,它会创建一个新的字符串:

// Implementation of the trim method in the String class
new String(Arrays.copyOfRange(val, index, index + len),
                          LATIN1);

结尾

最后,我们来总结一下文本的一些关于 String 需要记住的几点:

  1. 字符串是不可变的,因此字符串的状态不能被改变。
  2. 为了节省内存,JVM 会将字符串保存在字符串池中。当创建一个新的字符串时,JVM会检查其值,并将其指向一个已存在的对象。如果池中不存在具有该值的字符串,则JVM会创建一个新的字符串。
  3. 使用==操作符比较的是对象引用,而使用equals()方法比较的是字符串的值。这一规则适用于所有对象。
  4. 当使用new操作符时,即使在字符串池中已经存在具有相同值的字符串,也会在池中创建一个新的字符串对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蟹蟹蟹风流

期望和你分享一杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值