在逛 Stack Overflow 的时候,发现了一些访问量像喜马拉雅山一样高的问题,比如说这个:如何比较 Java 的字符串?访问量足足有 370万+,这不得了啊!说明有很多很多的程序员被这个问题困扰过。

PS:系列文章回顾:《Stack Overflow 上250万浏览量的一个问题:你对象丢了

我们来回顾一下提问者的问题:

截止到目前为止,我一直使用“”操作符来比较字符串,直到程序出现了一个 bug,需要使用 .equals() 方法来解决。这是为什么呢?“”操作符和 .equals() 方法之间有什么区别呢?

和提问者相反,在我刚开始学习 Java 的时候,比较字符串一直使用的是 .equals() 方法,因为不管是书本还是老师,都告诫我不要直接使用“==”操作符来比较,会出 bug。至于为什么,书本和老师都没有帮我搞清楚。

那借此机会,我就来梳理一下 Stack Overflow 上的高赞答案,我们来一起学习进步,打怪升级。

  • “==”操作符用于比较两个引用(内存中的存放地址)是否相等,它们是否是同一个对象。

  • .equals() 用于比较两个对象的内容是否相等。

怎么理解这两句话呢?我来举个不恰当又很恰当的例子。

有一对双胞胎,姐姐叫阿丽塔,妹妹叫洛丽塔。我们普通人的眼睛完全无法分辨谁是姐姐谁是妹妹,可她们的妈妈却可以轻而易举地辨认出。

.equals() 就好像我们普通人,看见阿丽塔以为是洛丽塔,看见洛丽塔以为是阿丽塔,看起来一样就觉得她们是同一个人;“==”操作符就好像她们的妈妈,要求更严格,观察更细致,一眼就能分辨出谁是姐姐谁是妹妹。

String alita = new String("小萝莉");
String luolita = new String("小萝莉"); System.out.println(alita.equals(luolita)); // true
System.out.println(alita == luolita); // false

就上面这段代码来说,.equals() 输出的结果为 true,而“==”操作符输出的结果为 false——前者没后者要求那么严格。

大家都知道,Java 的所有类都默认地继承着 Object 这个超类,该类有一个名为 .equals() 的方法,源码如下所示。

public boolean equals(Object obj) {
return (this == obj);
}

可以看得出,Object 类的 .equals() 方法默认采用的是“”操作符进行比较。假如子类没有重写该方法的话,那么“”操作符和 .equals() 方法的功效就完全一样——比较两个对象的内存地址或者对象的引用是否相等。

但实际情况中,有不少类重写了 .equals() 方法,因为比较内存地址太重了,不太符合现实的场景需求。String 类就重写了 .equals() 方法,源码如下所示。

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

可以看得出,如果两个字符串对象“==”,那么 .equals() 的结果就为 true;否则的话,就比较两个字符串的内容是否相等。

大家应该都知道了,创建字符串对象有两种写法,如下所示。

String luolita = "小萝莉";
String alita = new String("小萝莉");

第一种是在字符串常量池(存储在方法区)中创建对象,并将对象的引用赋值给 luolita。第二种是通过 new 关键字在堆中创建对象,并将对象引用赋值给 alita。

PS:字符串作为最基础的数据类型,使用非常频繁,如果每次都通过 new 关键字进行创建,会耗费高昂的时间和空间代价。Java 虚拟机为了提高性能和减少内存开销,就设计了字符串常量池:相同字面量的对象只有一个。

PPS:Java 虚拟机在执行程序的过程中会把内存区域划分为若干个不同的数据区域,如下图所示。

下面我们通过实际代码来看看字符串的比较。

第一种:

new String("小萝莉").equals("小萝莉") // --> true

.equals() 比较的是两个字符串对象的内容是否相等,所以结果为 true。

第二种:

new String("小萝莉") == "小萝莉" // --> false

“==”操作符左侧的对象存储在堆中,右侧的对象存储在方法区,所以返回 false。

第三种:

new String("小萝莉") == new String("小萝莉") // --> false

new 出来的两个对象肯定是不相等的,所以返回 false。

第四种:

"小萝莉" == "小萝莉" // --> true

字符串常量池中只会有一个对象,所以返回 true。

"小萝莉" == "小" + "萝莉" // --> true

由于“小”和“萝莉”都在字符创常量池,所以编译器会将其自动优化为“小萝莉”,所以返回 true。

经过大量实例的分析,我们可以得出如下结论(也是对提问者的回答):

  • 当比较两个字符串对象的内容是否相等时,请使用 .equals() 方法。

  • 当比较两个字符串对象是否相等时,请使用“==”操作符。

当然了,如果要进行两个字符串对象的内容比较,除了 .equals() 方法,还有其他可选的方法。

1)Objects.equals()

Objects.equals() 这个静态方法的优势在于不需要在调用之前判空。

public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}

如果直接使用 a.equals(b),则需要在调用之前对 a 进行判空,否则可能会抛出空指针 java.lang.NullPointerException

Objects.equals("小萝莉", new String("小" + "萝莉")) // --> true
Objects.equals(null, new String("小" + "萝莉")); // --> false
Objects.equals(null, null) // --> true String a = null;
a.equals(new String("小" + "萝莉")); // throw exception

2)String 类的 .contentEquals()

.contentEquals() 的优势在于可以将字符串与任何的字符序列(StringBuffer、StringBuilder、String、CharSequence)进行比较。

public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}

从源码上可以看得出,如果 cs 是 StringBuffer,该方法还会进行同步,非常的智能化。不过需要注意的是,使用该方法之前,需要确保比较的两个字符串都不为 null,否则将会抛出空指针。

再强调一点,.equals() 方法在比较的时候需要判 null,而“==”操作符则不需要。

System.out.println( null == null); // --> true

好了各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是人才,二哥必须要为你点个赞

Stack Overflow 上 370万浏览量的一个问题:如何比较 Java 的字符串?的更多相关文章

  1. [转帖]Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递?

    Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递? http://www.itpub.net/2019/12/03/4567/   在逛 Stack Overfl ...

  2. Stack Overflow上59万浏览量的提问:为什么会发生ArrayIndexOutOfBoundsException?

    在逛 Stack Overflow 的时候,发现了一些访问量像昆仑山一样高的问题,比如说这个:为什么会发生 ArrayIndexOutOfBoundsException?这样看似简单到不值得一问的问题 ...

  3. 为什么开发者热衷在Stack Overflow上查阅API文档?

    摘要:一项新研究跟踪了Android开发者的访问历史,发现开发者多达二分之一的文档是从Stack Overflow上获取到的,而Stack Overflow上的示例也多于官方指南,开发者通过搜索更多时 ...

  4. Stack Overflow 上排名前十的与API相关的问题

    Stack Overflow是一个庞大的编程知识仓库,在Stack Overflow 上,数百万的提问被回答,并且这些回答都是高质量的.这就是为什么在Google搜索结果的排行榜上,Stack Ove ...

  5. Stack Overflow上关于Java Collections的几个常见问题

    下面列出Stack Overflow上最常见的几个关于Java Collections的问题并给出答案. 1. 什么时候用LinkedList,什么时候用ArrayList? ArrayList是使用 ...

  6. JavaScript超越了Java,c,python等等成为Stack Overflow上最热门的

    JavaScript超越了Java,c,python等等成为Stack Overflow上最热门的标签 在2015年6月至今,JavaScript超越了Java,c,python等等成为Stack O ...

  7. Stack Overflow 上 250W 浏览量的一个问题:你对象丢了

    在逛 Stack Overflow 的时候,发现最火的问题竟然是:什么是 NullPointerException(java.lang.NullPointerException),它是由什么原因导致的 ...

  8. Stack Overflow 上人气最旺的 10 个 Java 问题

    1. 为什么两个(1927年)时间相减得到一个奇怪的结果? (3623个赞) 如果执行下面的程序,程序解析两个间隔1秒的日期字符串并比较: public static void main(String ...

  9. 关于Stack Overflow上ASP.NET最大连接数限制提问的一个思考

    原文地址:Why request queuing is high even when request executing is below its limit? We are using below ...

随机推荐

  1. 数据库系统(六)---MySQL语句及存储过程

    一.DDL.DML.DCL常用语句 1.DDL(Data Definition Language)数据库定义语言 (1)数据库模式定义 #创建数据库 create database if exsite ...

  2. DirectX9:基础篇 第一章 初始化Direct3D

    一.简介 二.Direct3D类 1.创建D3D类 IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion); //Direct3D类的创建 IDirec ...

  3. springcloud之Feign、ribbon设置超时时间和重试机制的总结

    一 超时时间配置 如果在一个微服务当中对同一个接口同时配置了Hystrix与ribbon两个超时时间,则在接口调用的时候,两个计时器会同时读秒. 比如,访问一个接口需要2秒,你的ribbon配置的超时 ...

  4. shell 队列实现线程并发控制

    需求:并发检测1000台web服务器状态(或者并发为1000台web服务器分发文件等)如何用shell实现? 方案一:(这应该是大多数人都第一时间想到的方法吧) 思路:一个for循环1000次,顺序执 ...

  5. [考试反思]0822NOIP模拟测试29:延续

    想保持优秀很困难 但是想持续垫底却很简单 但是你不想垫底的话持续垫底也很容易... 分AB卷,A卷共15人. skyh,tdcp,kx155,B哥145... 我:35,倒数第一. 板子专题,爆零快乐 ...

  6. NOIP 模拟赛 23 T4 大逃亡O(二分+广搜)(∩_∩)O

    题目描述 给出数字N(1≤N≤10000),X(1≤x≤1000),Y(1≤Y≤1000),代表有N个敌人分布一个X行Y列的矩阵上,矩形的行号从0到X-1,列号从0到Y-1再给出四个数字x1,y1,x ...

  7. 「POJ 3268」Silver Cow Party

    更好的阅读体验 Portal Portal1: POJ Portal2: Luogu Description One cow from each of N farms \((1 \le N \le 1 ...

  8. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

  9. 关于find的-perm

    关于find的-perm 参考关于find命令-perm 的用法 总结 有三种用法 find -perm -mode find -perm mode find -perm /mode(find -pe ...

  10. java多线程与线程并发四:线程范围内的共享数据

    当多个线程操作同一个共有数据时,一个线程对共有数据的改变会影响到另一个线程.比如下面这个例子:两个线程调用同一个对象的的方法,一个线程的执行结果会影响另一个线程. package com.sky.th ...