Java基础系列-深入理解==和equals的区别(一)
一、前言
说到==和equals的问题,面试的时候可能经常被问题到,有时候如果你真的没有搞清楚里边的原因,被面试官一顿绕就懵了,所以今天我们也来彻底了解一下这个知识点。
二、==和equals的作用
2.1 ==的作用
在java中我们用==来判断两个变量是否相等,但是会根据数据类型有所区别:
1.对于8种基础数据类型(byte、short、int、long、double、float、boolean、char)来说==是判断变量的数值是否相等。
byte y1 = 1, y2 = 1;
short s1 = 1, s2 = 1;
int i1 = 1, i2 = 1;
long l1 = 1, l2 = 1;
double d1 = 1, d2 = 1;
float f1 = 1, f2 = 1;
boolean b1 = true, b2 = true;
char c1 = 1, c2 = 1;
System.out.println('byte:y1==y2 ' + (y1 == y2));
System.out.println('short:s1==s2 ' + (s1 == s2));
System.out.println('int:i1==i2 ' + (i1 == i2));
System.out.println('long:l1==l2 ' + (l1 == l2));
System.out.println('double:d1==d2 ' + (d1 == d2));
System.out.println('float:f1==f2 ' + (f1 == f2));
System.out.println('boolean:b1==b2 ' + (b1 == b2));
System.out.println('char:c1==c2 ' + (c1 == c2));
对于引用类型,==比较的是引用的地址是否相等:
Object o1 = new Object();
Object o2 = o1;
Object o3 = new Object();
System.out.println('Object:o1 == o2 ' + (o1 == o2));
System.out.println('Object:o1.equals(o2) ' + (o1.equals(o2)));
System.out.println('Object:o1 == o3 ' + (o1 == o3));
System.out.println('Object:o1.equals(o3) ' + (o1.equals(o3)));
接下来我们来看看equals里的内部实现,其实还是调用的==:
总结:所以说对于==,当数据类型是8大基础类型时,比较的是(栈中)数值是否相等,当数据类型是引用类型时,比较的是对象的引用地址是否相等。这是通过jvm来自动判断的。
2.2 equals的作用
equals是object类中的一个方法,也就是说使用equals必须是对象类型,所以equals比较的是对象是否相当,默认情况上边已经说过了使用的就是==,但是别忘了equals是基类中的方法,是可以重写的,当我们重写的时候就可以实现不同的功能了。
那么对于字符串,也是一种引用类型,我们先来看看下面这个例子:
String str1 = '123',str2 = '123';
String str3 = new String('123');
String str4 = new String('123');
System.out.println('String:str1 == str2 ' + (str1 == str2));
System.out.println('Object:str1.equals(str2) ' + (str1.equals(str2)));
System.out.println('Object:str1 == str3 ' + (str1 == str3));
System.out.println('Object:str1.equals(str3) ' + (str1.equals(str3)));
System.out.println('Object:str3 == str4 ' + (str3 == str4));
System.out.println('Object:str3.equals(str4) ' + (str3.equals(str4)));
这里我们介绍一个概念,叫做字符串拘留池:由于字符串这种东西在我们的程序中非常的常用,每一个字符串都要创建对象,花费开销,但是通常字符串一般的我们只关心它的值是什么,所以jvm在这里做了一个优化,当字符串在拘留池(一块存储字符串的地方)里出现的时候,如果下次我们又用到了这个字符串,就直接返回池子里字符串的地址,因为值都是一样的,减少了开销,如果没有,就放进池子里,以备后来有用到的时候,相当于一个缓存了。
所以当我们执行String str1 = '123',str2 = '123'; 实际上两个变量指向的是同一个地址,所以自然的str1 == str2和str1.equals(str2)是一样的,都为true,没毛病。
当我们使用String str3 = new String('123');很明显示开辟了一块新的内存地址,只不过存储的值是123是一样的,所以str1 == str3是false,没毛病,但是按理说str1.equals(str3)也应该是false啊,之前不是说内部也是用的==吗,我们可以看一下字符串类中equals的实现:
很明显,在实现判断了地址是否相同以后,这里边又做了判断,字符串的值是否相等,如果相等就返回true,到这里为止,我们已经真相大白了,原来String类重写equals方法,判断的是字符串的值是否相等,而不仅仅是地址。所以自然的我们的str1.equals(str3) 是true了。
那么由此可知,str3 == str4 比较的是地址,明显不一样,因为是new了两个对象,所以返回false,str3.equals(str4)比较的是字符串的值,所以返回true,也没毛病。
总结:因为我们在对字符串比较的时候,往往关注的就是值一样不一样,所以java这样重写也是 很有必要的,所以我们一直推荐判断字符串相等用equals方法,而不是用==来判断。equals方法具体产生什么样的效果,完全看子类是怎么重写的,比如Date类,重写了equals方法,只要两个时间的getTime()时间戳一样,那么就相等,这也是符合我们的认知的。
所以,在我们以后需要判断两个对象相等的用equals的时候,我们可以完全根据自己的业务来重写equals方法,比如两个Person实例,如果id一样,我们就应该认为他们俩是相等,这个时候我们就可以重写equals方法,用id作比较来返回值。
三、总结
这次终于搞明白了==和equals的用法和作用,以后学什么东西还必须要深入理解一下内部原理才行,这样就不怕面试官再来搞事情了。