5. 请说出 == 和 equals 方法的区别是什么?Object.equals 的默认行为是什么?
== 和 equals 的区别,以及 Object.equals 的默认行为
一句话回答
==:基本类型比较值,引用类型比较内存地址(引用是否相同)。equals:Object的方法,默认行为和==一样,比较引用;但很多类(如String、Integer)重写后比较内容/逻辑相等。Object.equals默认行为:return this == obj;,即判断是否是同一个对象。
1. == 的语义
1.1 基本类型:比较值
int a = 10;
int b = 10;
System.out.println(a == b); // true
double d1 = 1.0;
double d2 = 1.0;
System.out.println(d1 == d2); // true
对于 byte、short、int、long、float、double、char、boolean,== 比较的是数值本身。
1.2 引用类型:比较地址
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false,两个不同对象
System.out.println(s1.equals(s2)); // true,内容相同
== 比较的是两个引用是否指向同一个对象(堆中同一块内存地址)。
1.3 字符串常量池的影响
String a = "hello";
String b = "hello";
String c = new String("hello");
System.out.println(a == b); // true,都指向常量池同一对象
System.out.println(a == c); // false,c 是堆上新对象
System.out.println(a.equals(c)); // true,内容相同
==为true不代表内容相同,只是恰好指向同一对象。
判断字符串内容,永远用equals。
2. equals 的语义
2.1 Object.equals 默认行为
Object 是所有类的父类,其 equals 源码:
public boolean equals(Object obj) {
return (this == obj);
}
默认行为就是 ==:判断两个引用是否指向同一个对象。
class Person {
String name;
}
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.equals(p2)); // false,默认比较地址
2.2 重写后的 equals
很多类重写了 equals,改为比较内容或业务逻辑:
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true,String 重写为比较字符内容
String.equals 源码简化:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
// 逐字符比较
return ...;
}
return false;
}
2.3 包装类的 equals
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
System.out.println(i1 == i2); // false,不同对象
System.out.println(i1.equals(i2)); // true,值相同
注意:
Integer缓存-128 ~ 127,此范围内==可能为true,超出则为false,容易踩坑。
包装类比较值,一律用equals,或先拆箱。
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true,缓存
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false,超出缓存
3. 核心对比
| 维度 | == |
equals |
|---|---|---|
| 类型 | 运算符 | 方法 |
| 基本类型 | 比较值 | 不能用于基本类型 |
| 引用类型 | 比较地址 | 默认比较地址,可重写为比较内容 |
| 是否可重写 | 不能 | 可以 |
| 所属 | 语言层面 | Object 定义,可覆盖 |
| 典型用途 | 判断是否同一对象 | 判断逻辑是否相等 |
| 性能 | 快 | 通常较慢,可能逐字段比较 |
记忆:
==比地址,equals比内容(前提是重写了)。
基本类型只能用==,引用类型判断内容用equals。
4. 重写 equals 的规范
重写 equals 必须满足以下契约(来自 Object 文档):
- 自反性:
x.equals(x)必须为true。 - 对称性:
x.equals(y)为true,则y.equals(x)也必须为true。 - 传递性:
x.equals(y)、y.equals(z)为true,则x.equals(z)也必须为true。 - 一致性:只要对象状态没变,多次调用结果一致。
- 非空性:
x.equals(null)必须为false。
重写示例
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
重写 equals 必须同时重写 hashCode
原因:
HashMap、HashSet先用hashCode定位桶,再用equals比较。- 如果两个对象
equals为true,但hashCode不同,会违反约定,导致集合行为异常。
约定:
equals为true,hashCode必须相同。hashCode相同,equals不一定为true。
Map<Person, String> map = new HashMap<>();
Person p1 = new Person("Tom", 18);
Person p2 = new Person("Tom", 18);
map.put(p1, "x");
System.out.println(map.get(p2)); // 若正确重写 equals 和 hashCode,输出 x
5. 常见坑
5.1 字符串比较用 ==
// 错误
if (input == "yes") { ... }
// 正确
if ("yes".equals(input)) { ... }
用 "yes".equals(input) 还能避免 input 为 null 时的 NPE。
5.2 包装类比较用 ==
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
5.3 重写 equals 没重写 hashCode
Set<Person> set = new HashSet<>();
set.add(new Person("Tom", 18));
System.out.println(set.contains(new Person("Tom", 18))); // 可能 false
5.4 equals 参数类型写错
// 错误:参数类型不是 Object,变成重载而非重写
public boolean equals(Person o) { ... }
// 正确
@Override
public boolean equals(Object o) { ... }
加 @Override 可让编译器帮你检查。
5.5 对 null 调用 equals
String s = null;
s.equals("x"); // NPE
"x".equals(s); // false,安全
Objects.equals(s, "x"); // false,安全
6. 面试口述版
== 是运算符,基本类型比较值,引用类型比较内存地址,也就是是否是同一个对象。
equals 是 Object 定义的方法,默认实现就是 this == obj,和 == 一样比较地址。但很多类重写了它,比如 String、Integer,改成比较内容或逻辑相等。
所以判断两个引用是否指向同一个对象用 ==,判断内容是否相等用 equals。字符串和包装类的值比较一定要用 equals,因为 == 可能因为常量池或缓存产生误导。
重写 equals 要满足自反、对称、传递、一致、非空五个契约,并且必须同时重写 hashCode,否则在 HashMap、HashSet 中会出现问题。
7. 常见追问
-
Object.equals默认行为是什么?
return this == obj;,比较引用是否相同。 -
为什么重写 equals 必须重写 hashCode?
保证equals相等的对象hashCode也相等,否则哈希集合行为异常。 -
String为什么要重写 equals?
字符串是值语义,业务上关心内容是否相同,而不是是否同一对象。 -
==能比较两个null吗?
能,null == null为true;但null.equals(...)会 NPE。 -
Integer的==为什么有时是true?
Integer缓存-128 ~ 127,此范围内自动装箱复用同一对象。 -
equals和hashCode的关系?
equals为true则hashCode必须相同;hashCode相同,equals不一定为true。 -
Objects.equals和a.equals(b)区别?
Objects.equals做了 null 安全判断,a为 null 时返回b == null,不会 NPE。

浙公网安备 33010602011771号