抛砖引玉
首先,让我们通过一段简单的Java代码来进行思考和猜测。请大家看以下示例代码,并猜测每个输出语句会打印出什么结果。
public static void main(String[] args) {
int i1 = 1;
int i2 = 1;
String s1 = "Hello World";
String s2 = new String("Hello World");
String s3 = s2;
// 两个int类型变量比较
System.out.println("i1 == i2:" + (i1 == i2));
// 三个字符串两两用“==”比较
System.out.println("s1 == s2:" + (s1 == s2));
System.out.println("s1 == s3:" + (s1 == s3));
System.out.println("s2 == s3:" + (s2 == s3));
// 三个字符串两两用“equals”方法比较
System.out.println("s1 equals s2:" + s1.equals(s2));
System.out.println("s1 equals s3:" + s1.equals(s3));
System.out.println("s2 equals s3:" + s2.equals(s3));
}
现在,让我们一起运行main
方法,查看实际的输出结果:
i1 == i2:true
s1 == s2:false
s1 == s3:false
s2 == s3:true
s1 equals s2:true
s1 equals s3:true
s2 equals s3:true
你猜对了吗?接下来,我们将深入分析这段代码,解释为什么会出现这样的结果。
庖丁解牛
在上述代码中,涉及了两种数据类型:基本数据类型和引用类型。我们首先来了解它们在堆栈内存中的存放方式,如下图所示。
基本数据类型直接分配在栈内存中,如
int i1 = 1;
和int i2 = 1;
。引用类型创建的对象在堆内存中分配,而栈内存中的变量存储对对象的引用地址。例如,
String s2 = new String("Hello World");
中,s2
在栈中存储对字符串对象的引用地址xf002
。
有了这个基础知识,我们继续分析代码。
基础数据类型比较
首先,对于基本数据类型,使用 ==
运算符比较的是它们在栈内存中的值。因此,int i1 == i2
返回 true
,因为它们的值相等。
引用类型比较
接下来,我们关注引用类型的比较。细心的同学可能已经注意到了关于String对象的创建其实是采用了两种方式:一种是直接赋常量值,一种是通过new
关键字进行创建,如下所示。
// 创建了一个字符串常量,存储在常量池中,而 s1 存储了对该常量的引用。
String s1 = "Hello World";
// 使用 new 关键字创建了一个字符串对象,存储在堆内存中,s2 存储了对堆中对象的引用。
String s2 = new String("Hello World");
// 将 s2 赋值给 s3,使它们共享相同的引用。
String s3 = s2;
引用类型 == 比较
使用==
运算符比较两个引用时,比较的是它们在栈内存中存储的引用地址。因此,s1 == s2
和s1 == s3
返回false
,因为它们分别指向常量池和堆内存中的不同对象;s2 == s3
返回true
,因为它们指向相同的对象。
引用类型 equals 比较
在Java中Object
是类中之王
,所有类都默认继承自该类。它自身有默认的equals
方法,源码如下所示。
public boolean equals(Object obj) {
return (this == obj);
}
可以看到,Object
类的equals
方法其实就是采用的==
方式来比较两个对象是否相等,所以默认情况下equals
与==
没啥区别。
既然这样的话,那么三个字符串之间采用equals
方法跟采用==
不应该是一样的吗,为啥用equals
比较全部返回true
呢。
因为String
类重写了Object
类的equals
方法,具体源码实现如下所示。
/**
* 将当前字符串与另一个对象进行比较。
* 如果两者相等(内容相同),则返回 true,否则返回 false。
*
* @param anObject 要比较的对象
* @return 如果当前字符串与指定对象相等,返回 true,否则返回 false
*/
public boolean equals(Object anObject) {
// 判断是否是同一个对象引用
if (this == anObject) {
return true;
}
// 判断传入的对象是否是一个字符串
if (anObject instanceof String) {
// 将传入的对象转换为字符串
String anotherString = (String)anObject;
// 获取当前字符串和另一个字符串的字符数组
char v1[] = value;
char v2[] = anotherString.value;
// 获取当前字符串和另一个字符串的长度
int n = value.length;
// 如果两个字符串的长度相等,进行字符逐一比较
if (n == anotherString.value.length) {
int i = 0;
while (n-- != 0) {
// 如果有任何字符不相等,则返回 false
if (v1[i] != v2[i])
return false;
i++;
}
// 所有字符都相等,返回 true
return true;
}
}
// 传入的对象不是字符串,或者长度不相等,返回 false
return false;
}
String
类重写了Object
类的equals
方法,实现了逐字符比较字符串内容的逻辑。因此,使用equals
方法比较字符串对象时,比较的是它们的内容而不是引用地址。因此,s1.equals(s2)
、s1.equals(s3)
和s2.equals(s3)
全部返回true
,因为它们的内容都是相同的。
总结
-
使用 ==
比较基本数据类型时,比较的是它们的值。 -
使用 ==
比较引用类型时,比较的是它们在栈内存中存储的引用地址。 -
equals
方法通常被重写用于比较对象的内容,而不是引用地址。在String
类中,equals
方法比较的是字符串的字符内容。

原文始发于微信公众号(Java浩窍门):面试官:'==' 与 'equals()',你真的懂吗?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/244542.html