前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >深入浅出 Java 字符串比较

深入浅出 Java 字符串比较

原创
作者头像
前端修罗场
发布2024-12-22 19:29:17
发布2024-12-22 19:29:17
1340
举报

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

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

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

String 类

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

代码语言:java
复制
public String(char value[]) {
    this(value, 0, value.length, null);
}

方法重载和 String 类

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

代码语言:java
复制
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变量,但在内存堆中只创建并存储了两个对象。为了证明这一点,请查看以下代码。)

代码语言:java
复制
String juggy = "Juggy";
String anotherJuggy = "Juggy";
System.out.println(juggy == anotherJuggy);

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

new 操作符

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

代码语言:java
复制
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()方法的描述:

代码语言:java
复制
    /**
     * 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 关键字强制创建两个字符串时会发生什么:

代码语言:java
复制
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()方法进行字符串比较:

代码语言:java
复制
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 方法来比较对象引用也可能很棘手。关键是,如果该方法改变了字符串中的某些内容,那么对象引用将会不同。

例如这个例子:

代码语言:java
复制
System.out.println("duke".trim() == "duke".trim());; 

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

那么,再换一个写法:

代码语言:java
复制
System.out.println(" duke".trim() == "duke".trim()); 

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

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

代码语言:java
复制
// 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操作符时,即使在字符串池中已经存在具有相同值的字符串,也会在池中创建一个新的字符串对象。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • String 类
  • 方法重载和 String 类
  • 字符串池
  • new 操作符
  • intern()
  • 使用 String 类的 equals() 方法
  • 使用 String 时的常见错误
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档