== 与 equals() 只看字面意思都是相等的意思,但是在用法上确有些许差异,对于即使多年经验的开发者,如果不是经常使用,在使用时候也可能因为不知道其差异内涵而造成使用的失败。因此建议收藏,无论是对于之后的开发还是以后的面试都会有用。
==方法的使用含义:
在基本数据类型和引用数据类型之间有所差异:
对于基本数据类型(byte,short,char,int,float,double,long,boolean)来说,主要是用来比较他们的值是否相同,及时两个数据的基本数据类型不相同,那么也会返回true。这是因为这些基本数据类型在常量池中是以HashSet的形式存储起来的。
对于对象引用类型来说,主要来比较在堆内存中的位置是否相同。如果当两个引用变量的类型具有父子关系,只有当两个对象指向同一个对象时候,才会返回true。
另外对于基本数据类型的包装类(Byte, Short, Character,Integer,Float, Double,Long, Boolean)中,除了基本Float与Double之外,其他六种都是实现了常量池,就是说我们可以通过==来判断是否相同。
equals()方法:
在了解了“==”的使用方法后我们来查看下equals方法的含义,我们知道Object方法是所有方法的父类,而equils() 方法就是Object类的一个方法。下面是Object方法中的equils() 方法源码:
public boolean equals(Object obj) {
return (this == obj);
}
可以看出来实际上使用的就是“==”判断的结果。但是并不是所有的对象都是使用这种方式来进行比较,有些类会重写了equils() 方法,此时使用的方法就是自身重写的equils() 方法。因此在使用equils() 进行比较时需要跟进对象是否重写了equils() 方法来进行判断。下面我们可以看下我们在String类型的的对象中常用的equils() 方法的源码:
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;
}
从String类中的equals() 方法可以看出,其就是比较两个字符串的内容是否相同。因此之后我们如果需要比较两个字符串内容是否相同就可以直接使用equils() 方法来比较判断。
因此要判断equals() 方法我们就要查看该对象是否重写了此方法,没有的话我们默认比较地址值是否相同。在实际的开发中我们也可以根据自己的需求,来重写equals() 方法,来满足自己的实际业务需求。
典型案例及面试题:
1.接下来看下我们对上面的分析是否完全理解,我们运行如下demo(相应的结果见注释):
public class test {
public static void main(String args[]) {
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = str2; // 引用传递
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
System.out.println(str2.equals(str3)); // true
}
}
如上例子中我们看到,使用“==”以及“equals() ”比较str1与str2时候发现分别返回不同的值,如小伙伴还是不理解的可以结合上面的讲解结合对堆内存空间和栈内存空间的理解,并分析下面的图例来进行思考理解。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oT1CEl8o-1628779679955)(C:\Users\Garth\AppData\Roaming\Typora\typora-user-images\image-20210713211545415.png)]
2.结合String方法的intern()来进行分析
public class test{
public static void main(String[] args) {
String s1 = "Hello";
String s2 = new String("Hello");
s2 = s2.intern();
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2)); // true
}
}
注:intern() 方法返回字符串对象的规范化表示形式。它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
上述代码的第5行中,java.lang.String的intern()方法"abc".intern()方法的返回值还是字符串"abc",表面上看起来好像这个方法没什么用处。但实际上,它做了个小动作:检查字符串池里是否存在"abc"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会 把"abc"添加到字符串池中,然后再返回它的引用。
3.包装类Integer 的特殊性
public class test {
public static void main(String args[]) {
Integer a1=127;
Integer a2=127;
System.out.println(a1==a2); //true
Integer a3=128;
Integer a4=128;
System.out.println(a3==a4); //false
}
}
之前我们讲解到有些基本数据类型的包装类通常也可以使用==来判断是否相同,但是这只是“通常”!其实Integer 在常量池中的存储范围为[-128,127],当一旦超过这个范围,便会在堆中新建一个对象来保存这个值,所以会指向不同的地址,因此最终结果会为false。
4.接下来我们再看一道典型题目:
public static void main(String[] args) {
Integer s1 = 127;
int s2 = 127;
Integer s3 = new Integer(127);
Integer s4 = 127;
System.out.println(s1.equals(s2)); //true
System.out.println(s1 == s2); //true
System.out.println(s1.equals(s3)); //true
System.out.println(s1 == s3); //false
System.out.println(s1 == s4); //true
s1 = 128;
s2 = 128;
s3 = new Integer(128);
s4 = 128;
System.out.println(s1.equals(s2)); //true
System.out.println(s1 == s2); //true
System.out.println(s1.equals(s3)); //true
System.out.println(s1 == s3); //false
System.out.println(s1 == s4); //false
}
首先我看查看Integer源码,发现此类重写了equils()方法,代码如下
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
因此使用此方法时候实际比较的是内存中最终指向的值,而不是对象的位置,而new一个对象时候内存会分配一个地址,再结合Integer以上讲解的在-128与+127之间与非此范围的内存中的位置,最终结果并不难理解。
【参考】
参考博客:
https://www.cnblogs.com/qianguyihao/p/3929585.html
https://blog.csdn.net/qq_37358860/article/details/100149986