Java 中 == 与 equals() 的区别
1. 一句话总结
==:基本类型比值 ;引用类型比地址(判断是否指向堆中同一个对象)。equals():是一个方法。默认实现(Object类)也是比地址 ;但String、包装类等通过重写 实现了内容/逻辑相等的比较。
核心金句 :
==判断"是不是同一个";equals()判断"长得像不像"(取决于是否重写)。
2. 核心区别对比表
| 特性 | == |
equals() |
|---|---|---|
| 本质 | Java 运算符 | Object 类的方法 |
| 比较内容 | 基本类型比数值 ,引用类型比内存地址 | 默认比地址 ,重写后比对象内容 |
| 可定制性 | 不可重写,行为固定 | 可以重写,自定义逻辑相等规则 |
| 适用范围 | 任何基本类型或对象引用 | 仅限对象(不能用于基本类型) |
| null 处理 | null == null 为 true |
null.equals(...) 会抛 NPE |
3. == 的用法
3.1 基本类型比较(比较值)
java
int a = 10;
int b = 10;
System.out.println(a == b); // true
char c1 = 'A';
char c2 = 'B';
System.out.println(c1 == c2); // false
3.2 引用类型比较(比较地址)
java
String s1 = new String("Java");
String s2 = new String("Java");
String s3 = s1;
System.out.println(s1 == s2); // false(不同对象)
System.out.println(s1 == s3); // true(同一引用)
4. equals() 的用法
4.1 默认行为:Object.equals() 等同于 ==
java
public boolean equals(Object obj) {
return (this == obj);
}
4.2 重写后的行为:比较内容/逻辑相等
String 重写了 equals(),比较字符串内容:
java
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1.equals(s2)); // true
5. 进阶高频考点(建议必背)
5.1 字符串常量池(String Pool)
面试常考:为什么 a == b 有时是 true,有时是 false?
java
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a == b); // true(常量池同一对象)
System.out.println(a == c); // false(new 在堆上创建新对象)
System.out.println(a.equals(c)); // true(内容相等)
补充:
java
String d = c.intern();
System.out.println(a == d); // true(intern 返回常量池引用)
5.2 包装类缓存机制(Integer Cache)
Integer、Short、Byte、Character、Long(部分范围)存在缓存机制。
java
Integer x = 127;
Integer y = 127;
Integer m = 128;
Integer n = 128;
System.out.println(x == y); // true (缓存 -128~127)
System.out.println(m == n); // false (超出缓存范围)
System.out.println(m.equals(n)); // true
结论 :比较包装类型的值,优先用 equals()(或拆箱成基本类型再比 ==,注意 null 拆箱会 NPE)。
5.3 equals() 与 hashCode() 的契约(HashMap/HashSet 面试点)
- 两个对象如果
equals()为true,那么它们的hashCode()必须相同。 HashMap/HashSet的基本流程:先hashCode()定位桶,再equals()精确比较。- 如果只重写
equals()不重写hashCode():可能出现"放得进去、取不出来"或"重复放入"的问题。
6. equals() 正确重写规则(契约)
- 自反性 :
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。
7. instanceof 还是 getClass()?(加分点)
getClass():要求必须同一具体类才相等,更严格,较不易破坏对称性。instanceof:允许子类参与比较,更灵活,但继承体系下容易破坏对称性/传递性。
8. null 安全:避免 NPE 的写法(实用)
8.1 容易出错
java
String s = null;
System.out.println(s.equals("a")); // NPE
8.2 推荐写法
java
String s = null;
System.out.println("a".equals(s)); // false
或:
java
System.out.println(java.util.Objects.equals(s, "a")); // false(null-safe)
9. 一个规范的重写示例(可背模板)
java
import java.util.Objects;
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = 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);
}
}
10. 什么时候用 ==?什么时候用 equals()?(面试收尾)
- 基本类型:用
==。 - 判断两个引用是否指向同一对象:用
==。 - 判断对象内容(业务逻辑相等):用
equals()(并确保类正确重写equals/hashCode)。 - 比较
enum:推荐使用==(枚举天然单例,且语义明确)。
11. 常见追问补充(可选)
StringBuilder/StringBuffer的equals():没有重写,仍是地址比较;要比内容用toString()。- 数组的
equals():数组没有重写equals(),比较地址;比内容用Arrays.equals()/Arrays.deepEquals()。 - 浮点数比较 :
==可能因精度误差不可靠,业务上常用误差范围(epsilon)比较。