Java字符串比较

本文为翻译的文章,作者Rafael Chinelato Del Nero,原文:

https://www.javaworld.com/article/3276354/java-language/java-challengers-2-string-comparisons.html

在字符串池中,字符串方法、关键字和操作符如何来处理比较。

在Java语言中,String类封装了一个char类型的数组。简单来说,String是一个字符串数组,用来构成词语,语句,或者其他任何你想要的数据。

封装是面向对象编程中最强大的概念之一。因为有了封装,你不需要知道String类是如何工作的,你只需要知道使用接口中的哪个方法。

当你观察Java中的String类,你可以看到char数组是如何被封装的:

为了更好的理解封装,考虑一个实际的对象:汽车。为了驾驶它,你需要知道汽车内部是如何运转的吗?当然不,但你需要知道汽车提供了什么交互:比如象油门,刹车,方向盘这样的东西。每个交互都支持某种特定的操作:加速,刹车,向左转,向右转。面向对象编程也是一样的。

我在Java挑战者系列的第一篇博客中介绍了方法重载,String类广泛地使用了这种技术。重载能够让你的类变得真正的灵活,包括String:

这篇Java挑战者不是要尝试理解String类如何工作,而是帮助你理解它做了什么,以及如何在你的代码中来使用它。

什么是字符串池?

String可能是Java中用得最多的类了。如果每次我们使用一个String的时候,都在堆内存中创建一个新的对象,我们可能会浪费很多内存。对于每个字符串的值,字符串池都只保存了一个对象,通过这样的方式来解决上述问题,如下图所示:

图1 字符串池中的字符串

尽管我们创建了Duek和Juggy的字符串变量,但只有两个对象被创建并保存在堆内存中。为了证明这一点,看看下面的代码样例。(回忆一下,Java中“==”操作符被用来比较两个对象,并确定它们是不是一样的。)

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

一个例外:“new”操作符

现在看看这段代码----它看起来与前面的示例代码比较类似,但有一点差别。

基于前面的样例代码,你可能会认为这段代码会返回true,但它确实是fasle。添加了new操作符,强制在堆内存中创建一个新的String。因而,JVM创建两个不同的对象。

本地方法

Java中的本地方法将会使用C语言来进行编译,通常是为了操纵内存和优化性能的目的。

字符串池和intern()方法

我们使用一种被称为String驻留的技术,把字符串保存在字符串池中。Javadoc告诉我们关于intern()方法的说明:

intern()方法用来把字符串保存在字符串池中。首先,它校验你创建的字符串是池中是否已经存在。如果不存在,它在池中创建一个新的字符串。 在这背后,字符串池的逻辑是基于享元模式的。

现在,请留意当我们使用new关键字来强制创建两个字符串的时候,到底发生了什么:

与前面样例中的new关键字不同的是,这个案例返回了true。这是因为使用intern()方法确保字符串将会被保存在池中。

String类中的Equals方法

equals()方法被用来验证两个Java类的状态是否相同。 因为equals()方法来自Object类,每个Java类都继承它。 但是equals()方法必须要重写才能让它正确地工作。当然,String类重写了equals()

来看一下:

就像你看到的,equals方法是比较字符串的状态而不是对象引用。对象引用是否相同没有关系,字符串的状态将会被比较。

最常用的String方法

在接受字符串比较的挑战之前,最后还有一件你需要知道的事情。想想String类中的这些通用方法:

接受字符串比较的挑战!

让我们快速挑战一下你所学的关于String类的知识。在这个挑战中,你将会使用我们探讨的概念来比较很多字符串。看看下面的代码,你能够确定每个result变量的最终值吗?

下面哪个答案是result变量的最终结果:

A: 02468

B: 12469

C: 12579

D: 12568

刚才发生了什么?理解String的行为

代码的第一行我们看到:

尽管调用trim()方法后的字符串是相同的, 但" powerfulCode "字符串与开始不一样了。在这个案例中,比较返回了false,因为trim()方法移除了两边的空格,导致它使用new操作符强制创建了一个新的字符串。

接下来,我们看看:

这不神秘,在字符串池中,这些字符串是相同的。这个比较返回true。

再接着:

使用new保留字来强制创建两个新的字符串对象,不管它们是否相等。在这个案例中,比较返回了false,即使字符串的值是相同的。

接下来是:

因为我们使用equals()方法,字符串的值而不是对象实例将会被比较。在这个例子中,对象是否相同并没有关系,因为比较的是它们的值。这个比较返回true。

最后,我们有下面的代码:

就像你之前看到的那样,intern()方法把字符串放到了字符串池中。两个字符串都指向了同一个对象,所以这个例子中的比较返回了true。

字符串的常见错误:

想知道两个字符串是否指向同一个对象比较困难,特别是当两个字符串包含了相同的值的时候。记住,使用保留字new总会创建一个新的对象,即使值是相同的。

使用String的方法来比较Object引用也比较棘手。关键是, 如果方法改变了String中的某些东西,那个对象引用将会不同:

一些示例可以帮助澄清这个概念:

这个比较返回true,因为trim()方法并没有生成新的String。

在这个例子中,第一个trim()方法会生成一个新的String,因为方法会执行它的动作,所以引用不同。

最后,当trim()方法执行的时候,它创建了一个新的String:

关于字符串需要记住什么

String是不可变的,所以一个String的状态是不能被改变的

为了节省内存,JVM把String保留在字符池中。当一个新的String创建的时候,JVM检查它的值并指向一个已经存在的对象。如果池没有一个String包含这个值,JVM就创建一个新的String

使用==操作符来比较对象引用。使用equals()方法来比较String的值。 同样的规则适用于所有的对象。

当使用new操作符的时候,在字符池中将会创建一个新的String,即使有一个值相同的String。

原创文章,欢迎转载,但请注明出处。

欢迎大家关注本订阅号互联网全栈架构,长按下图即可。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180617G15Q6O00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券