参考自:
https://www.cnblogs.com/baotong-9396/p/7182906.html
http://www.cnblogs.com/panxuejun/p/5866869.html
如题
作为一个写惯了js的人来说,初次接触java会有各种不习惯。其中有一个比较显著地不能理解的现象就是在java中比较两个String值是否相等居然用equals()而不是==。貌似在js中,比较值相等一个==就解决了,多么舒服。为什么java不行呢?
首先,写过js的都知道,js支持=,==以及===三种等号,第一个代表赋值,第二个用来表示值是否相等,而第三个用来判断是否是完全相等的对象。但是!java里并没有===比较符。
在js中,
===可以判断两个对象是否完全相等,不仅比较值也比较了内存地址,类型等,而java中的==其实和js中的===一样。这种比较是针对两个String类型的变量的引用,也就是说如果两个String类型的变量,它们所引用同一个String对象(即指向同一块内存堆),则==比较的结果是true。
而当两个String对象通过equals()方法来进行比较时,其实就是对String对象所封装的字符串内容进行比较,也就是说如果两个String对象所封装的字符串内容相同(包括大小写相同),则equals()方法将返回true。(String对象继承自Object,并且对equals()方法进行了重写。)
示例一:
示例二:
为啥示例一和示例二打印输出不一样呢?
很简单的,示例一通过new关键字在内存里开辟了两处空间,使用==比较当然是false了。而示例二直接将String作为基本类型使用,因此虚拟机不会为它们分配新的内存空间,而是到String缓冲池中来寻找。
示例三:
这个也很好理解,将s1赋值给s2,其实就是让s2与s1引用同一个对象。
在见识了以上三种应用示例后,我们是不是有必要去看看equals()方法的源码实现,以方便我们更好的理解?
String.class
- 以示例一为例,通过
new关键字分别调用了public String(String original)构造函数,通过该构造函数对value和hash进行赋值。 - 随后先判断
s1,s2是不是引用同一个对象,是的话返回true不是继续下一步。 - 通过判断用来比较的第二个对象是不是String类型,之后通过强转进一步确保。
- 用一个变量
n来保存第一个String对象值的长度,然后先比较这两个String对象值长度是否相等,相等继续否则返回false。 - 将这两个String对象的值分别保存进两个
char类型的数组,通过循环对比相同下标对应的字符是否相等,存在一个不等就结束并返回false,如果全部都一一对应相等,那么就返回true。
对equals做个总结
以上主要对equals()的源码进行了分析,仔细观察跟compareTo()里面的源码很大程度上相似,感兴趣的可以去看看哦。
延伸
String.class里面的equals()重写覆盖自顶级对象Object,Object里面的equals()源码很简单,直接用==判断两个对象是不是相同。
如果两个Object类型的对象使用
equals()并返回true,说明它们是相同的,包括在内存中的存储地址,所以它们的hashcode也相同!!!
思考一下:如果两个非Object类型的对象(例如本文中的String)使用equals()并返回true,那么它们的hashcode是否相同呢?
示例四:
通过上述示例可以看到,s1与s2其实是对两个不同的对象的引用,它们仅仅是值相同罢了,但是它们却拥有着相同的hashcode!!!
那么,是不是equals相同hashcode一定相同呢?
先给出结论:
- 如果x.equals(y)返回“true”,那么x和y的hashCode()必须相等。
- 如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。
为什么会做出上面的判断呢?不妨看看源码实现吧。依然以String.class为例:
研读源码思考:
主要关注
hashCode()内部实现,我们发现它最终返回的是h,而h其实就是通过value值循环遍历累加得到的。那么如果两个对象调用equals相等,不就意味着它们的值相等吗?hashCode()也是通过值来计算的,这样能不等吗?
那么为什么说hashcode相等,对象未必相同呢?
我个人理解,虽然hashCode()依赖于value值,但是它并没有逐一去比较每个下标对应的值是否相同,仅仅是判断累加值,那么假设两个对象的value值不同但是累加值却相同不就造成了hashcode相同吗?可事实上这两对象并不相同!
