Java 最经典面试题:== 和 equals() 到底有什么区别?一文彻底搞懂!

学习 Java 时,几乎每个人都会遇到这样一道经典面试题:

Java 中==equals()有什么区别?

很多初学者都会回答:

==比较地址,equals()比较内容。”

这句话虽然对,但并不完整,甚至在某些情况下还是错的。

今天我们就从零开始,把这个问题彻底讲明白。


一、什么是==

==是 Java 的一个比较运算符

它既可以比较基本数据类型,也可以比较引用类型,但比较规则不同。


1、比较基本数据类型

对于基本数据类型(intdoublecharboolean等),==比较的是值是否相等

例如:

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

为什么?

==

比较的是:

是不是同一个对象?

答案:

不是。

所以:

false

equals()

比较的是:

字符串内容是否相同?

答案:

都是 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()是对象的方法,默认比较引用,但重写后通常用于比较对象内容是否相等。