你是否曾想过 Java 中的 String 方法、关键字和操作符是如何在字符串池中进行比较操作的?本文就带你了解了解!
Java 的 String 类封装了一个字节数组。这个字节数组可以转换为字符,在这种情况下,String 就变成了用来组成单词、句子或任何其他你想要的数据的字符数组。
在本文中,我们将介绍如何比较两个字符串是否相等。但是在开始之前,我会先介绍一些相关的概念。
当查看 Java中的 String 类时,可以看到字符数组是这样被封装的:
public String(char value[]) {
this(value, 0, value.length, null);
}
方法重载是 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,因为这两个字符串在字符串池中指向同一个对象, 它们的值是相同的。
这段代码看起来与之前的例子相似,但它有一个不同之处:
String duke = new String("duke");
String anotherDuke = new String("duke");
System.out.println(duke == anotherDuke);
基于前面的例子,你可能会期望这段代码返回 true
,但实际上它返回的是 false
。添加new 操作符会强制在内存堆中创建一个新的String对象。因此,JVM 将会创建两个不同的对象。
为了将字符串存储在字符串池中,我们使用了一种称为字符串驻留(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()
方法确保了字符串将被存储在字符串池中。
我们使用 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 类值的状态是否相等,而不是对象引用。即使对象引用不同也没关系,将会比较字符串的状态。
要判断两个字符串是否指向同一个对象可能会很困难,特别是当这两个字符串包含相同的值时。不过请记住,使用保留关键字 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 需要记住的几点:
==
操作符比较的是对象引用,而使用equals()
方法比较的是字符串的值。这一规则适用于所有对象。new
操作符时,即使在字符串池中已经存在具有相同值的字符串,也会在池中创建一个新的字符串对象。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。