浅谈Java中的equals和==
孙玉超
2019-11-05 13:40:45
0 评论
1290 浏览
0 收藏
0 赞
前言:不要盲目的相信网上的博客教程,我更建议自己动手分析代码!
印象中我曾经面试过。。。噗,我曾经被面试过这个问题,当时的面试官给我的答案是:equals比较的是值,==比较的是地址。如此不严谨的回答,让我对这家公司的技术水平产生怀疑!正如下面代码显示的一样,想知道Java中==和equals的区别,我们还是应该先去根类Object看一下它的equals方法是怎么实现的。显而易见,其实在Object类中,equals方法返回的其实就是==的比较结果。
其实呢,在对Java基础体系比较熟悉的情况下。我们就会知道这个问题并不能简单的回答说equals比较的是值而==比较的是地址就结束了。这么回答只会让人觉得水平不扎实,或者说对于这个问题根本不清楚。我们都知道equals是根类Object中的方法。其他类库基本上都重写了这个方法,比如String类
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对象。也就是说如果两个String对象的地址相同,那么直接返回true,这段逻辑就是Object类中的那段代码。下面的代码对两个String对象进行字符遍历对比,每一个索引位置(字符串在内存中是一个字符数组)上的字符是不是相同。那么就很清楚了,equals到底如何比较取决于实现类如何重写。
说道这里不得不提一下,自我看过的所有相关文章,基本上说的都是对于基本类型(int,byte,……)“==” 比较的是值,引用类型比较的是地址,我是不同意这种理解的。我们知道Java中其实在一个方法内定义一个基本类型变量并初始化之后,再次定义一个相同字面量的变量其实并不会再次在栈中创建。例如:
void test(){ int a = 9; int b = 9;//由于栈中已存在,就直接使用 }
所以对于基本类型,“==” 也是比较地址,它们在栈帧中位于同样的地址(是在局部变量表)。值得注意的是,此时修改b的值并不会影响a,因为局部变量表会新开一个槽位。
那么可能有人会问了,不同方法之间的基本数据类型变量比较呢?要知道Java中每一个方法的执行都是一个栈帧入栈的过程,每一个方法结束都是一个栈帧出栈的过程,所以方法内的局部变量生命周期都是非常短暂的,如果两个变量能够比较,那么当运行时,他们一定处于同一个栈帧,因为栈是线程私有的空间,即使作为参数传递过来,那也是当前栈帧的一个变量。
那么问题又来了,当一个局部变量和一个成员变量都是基本数据类型,字面量相同时,我们用 “==” 也得出 true 的结果,但是很明显它们一个位于 栈,一个位于堆,这又反驳了上面我说的基本数据类型比较的也是地址。所以这个问题也是笔者一直想不通的地方,等大佬赐教……
其他类的equals方法也都大相庭径,关键是看它重写的逻辑。这里也可以去看Integer等等类的源码对比equals重写的逻辑。那有没有想过,当我们自定义的类调用equals方法呢?显而易见,如果不重写equals的话,那么就等价于==。如果重写的话,那么就取决于我们重写的逻辑,但是这里又牵扯到重写equals方法时,hashcode方法的处理。我们说,重写equals方法一定要注意重写 hashcode() 方法。参考 为什么说重写equals()一定要重写hashcode()