Java 最经典面试题:== 和 equals() 到底有什么区别?一文彻底搞懂!
学习 Java 时,几乎每个人都会遇到这样一道经典面试题:
Java 中
==和equals()有什么区别?
很多初学者都会回答:
“
==比较地址,equals()比较内容。”
这句话虽然对,但并不完整,甚至在某些情况下还是错的。
今天我们就从零开始,把这个问题彻底讲明白。
一、什么是==?
==是 Java 的一个比较运算符。
它既可以比较基本数据类型,也可以比较引用类型,但比较规则不同。
1、比较基本数据类型
对于基本数据类型(int、double、char、boolean等),==比较的是值是否相等。
例如:
inta=10;intb=10;System.out.println(a==b);输出:
true因为:
a 的值:10 b 的值:10两个值相同,所以返回true。
再例如:
doublex=3.14;doubley=3.14;System.out.println(x==y);结果仍然是:
true因此:
基本数据类型使用
==比较的是值。
2、比较引用类型
对于对象来说,==比较的是两个引用是否指向同一个对象。
例如:
Strings1=newString("Java");Strings2=newString("Java");System.out.println(s1==s2);输出:
false为什么?
因为内存中实际上创建了两个对象。
堆内存 +-------------+ s1 -->| "Java" | +-------------+ +-------------+ s2 -->| "Java" | +-------------+虽然内容一样,但它们是两个不同的对象。
所以:
s1 == s2比较的是:
两个引用是否指向同一个对象?答案是:
不是因此返回:
false所以:
引用类型使用
==比较的是对象地址(更准确地说,是比较引用是否指向同一个对象)。
二、什么是equals()?
equals()是Object类中的一个方法。
源码如下:
publicbooleanequals(Objectobj){return(this==obj);}可以发现:
Object 默认实现的equals()本质上还是使用==。
也就是说:
默认情况下:
obj1.equals(obj2)等价于:
obj1==obj2因此:
如果一个类没有重写
equals(),那么equals()和==的效果完全一样。
三、为什么还要有equals()?
因为很多时候,我们更关心的是:
内容是否相同?而不是:
是不是同一个对象?例如:
Strings1=newString("Java");Strings2=newString("Java");虽然:
s1和:
s2不是同一个对象。
但是:
字符串内容都是 Java。这时候:
System.out.println(s1.equals(s2));输出:
true为什么?
因为String类重写了equals()方法。
源码思想可以简化理解为:
publicbooleanequals(Objectobj){比较两个字符串长度; 再比较每一个字符; 全部相同返回true;}因此:
"Java" 和 "Java"内容一样,所以返回:
true四、什么时候==和equals()结果不同?
来看一个经典例子。
Strings1=newString("abc");Strings2=newString("abc");System.out.println(s1==s2);System.out.println(s1.equals(s2));输出:
false true为什么?
==
比较的是:
是不是同一个对象?答案:
不是。所以:
falseequals()
比较的是:
字符串内容是否相同?答案:
都是 abc。所以:
true这也是 Java 面试最经典的一道题。
五、为什么有时候==又是 true?
例如:
Strings1="Java";Strings2="Java";System.out.println(s1==s2);输出:
true很多初学者会疑惑:
不是说==比较地址吗?
为什么这里又是true?
原因是:
Java 有字符串常量池(String Pool)。
当写:
Strings1="Java";Strings2="Java";实际上:
字符串常量池 +---------+ | "Java" | +---------+ ↑ ↑ s1 s2两个变量都指向常量池中的同一个字符串对象。
因此:
s1 == s2结果就是:
true如果使用:
newString("Java")就会重新创建对象。
因此:
Strings1=newString("Java");Strings2=newString("Java");内存变成:
堆 +--------+ +--------+ | "Java" | | "Java" | +--------+ +--------+ ↑ ↑ s1 s2自然:
s1 == s2就是:
false六、自定义对象为什么 equals() 还是 false?
例如:
classStudent{Stringname;Student(Stringname){this.name=name;}}创建两个对象:
Students1=newStudent("Tom");Students2=newStudent("Tom");System.out.println(s1.equals(s2));输出:
false为什么?
因为:
Student 没有重写equals()。
它继承的是:
Object.equals()而:
Object.equals()底层就是:
this==obj比较地址。
因此:
false七、如何让 equals() 比较内容?
只需要重写equals()。
例如:
importjava.util.Objects;classStudent{Stringname;Student(Stringname){this.name=name;}@Overridepublicbooleanequals(Objectobj){if(this==obj){returntrue;}if(obj==null||getClass()!=obj.getClass()){returnfalse;}Studentother=(Student)obj;returnObjects.equals(name,other.name);}}现在:
Students1=newStudent("Tom");Students2=newStudent("Tom");System.out.println(s1.equals(s2));输出:
true因为:
现在比较的是:
name 是否相同。这也是实际开发中最常见的做法。
八、为什么重写 equals() 通常还要重写 hashCode()?
这是很多面试都会追问的问题。
例如:
HashSet<Student>set=newHashSet<>();如果:
两个对象:
equals()返回:
true但是:
hashCode()不同。
那么:
HashSet 仍然可能把它们当成两个不同对象。
因此 Java 官方规定:
如果重写了 equals(),通常也必须重写 hashCode(),保证相等对象具有相同的哈希值。
IDE(如 IntelliJ IDEA)通常也会提示同时生成这两个方法。
九、面试怎么回答?
如果面试官问:
Java 中
==和equals()有什么区别?
可以这样回答:
==是比较运算符。对于基本数据类型,它比较的是值;对于引用类型,它比较的是两个引用是否指向同一个对象。equals()是Object类的方法,默认实现也是比较引用,但很多类(例如 String、Integer、自定义实体类)都会重写equals(),用于比较对象内容是否相等。因此,是否比较内容,取决于该类是否重写了equals()方法。
这是比较标准、完整的面试回答。
十、总结
最后用一张表总结:
| 比较方式 | 基本数据类型 | 引用类型 |
|---|---|---|
== | 比较值 | 比较是否指向同一个对象 |
equals() | 不适用(基本类型不能调用方法) | 默认比较引用;重写后通常比较内容 |
记住下面四句话即可:
==比较基本类型时比较值。==比较对象时比较引用是否指向同一个对象。equals()默认和==一样,因为 Object 的实现本质就是==。- 很多类(如 String)重写了
equals(),因此equals()往往用于比较对象内容。
一句话总结:
==是 Java 的比较运算符,比较值或对象引用;equals()是对象的方法,默认比较引用,但重写后通常用于比较对象内容是否相等。