java中==和.equals()区别
前言
这是一个 Java 领域经典的基础问题。
一句话核心区别:
==比较的是"是不是同一个东西"(内存地址),
.equals()比较的是"长得一不一样"(内容/数值)。
Java 内存可以将其简单分为两块:
- 栈内存(Stack): 存放基本数据类型的值,以及对象变量的"引用地址"(可以理解为开门的钥匙)。
- 堆内存(Heap): 使用
new关键字创建的具体对象(可以理解为具体的房子),存放在这里。

一、 核心概念:物理地址 vs 逻辑内容
1.1 == 操作符:物理视角的"唯一性"
== 是 Java 的原生运算符,它执行的是最底层的"位比对"。
- 基本数据类型 :比较的是值。
- 引用数据类型 :比较的是内存地址(即堆内存中的物理位置)。
1.2 .equals() 方法:逻辑视角的"相似性"
.equals() 是 java.lang.Object 类中定义的成员方法。
- 默认逻辑 :如果类没有重写该方法,其源代码内部依然是使用
==进行比较。 - 重写逻辑 :Java 官方类(如 String, Integer, Date)都重写了此方法,将其改为内容比较。
二、可视化分析
2.1 比较基本数据类型(int, double, boolean 等)
基本类型的值直接存在栈中,因此 == 直接比较其数值是否相等。
java
int a = 100;
int b = 100;
// 结果是 true,因为数值都是 100
System.out.println(a == b);

2.2 比较引用数据类型(对象,如 String, Integer 等)
变量(钥匙)存在栈中,实际对象(房子)存在堆中。此时,== 比较的是这两把钥匙是不是同一把,即它们是否指向堆内存里的同一个物理地址。
java
// 在堆内存里建了一栋房子叫 "hello",钥匙交给了 s1
String s1 = new String("hello");
// 在堆内存里又建了一栋新房子也叫 "hello",钥匙交给了 s2
String s2 = new String("hello");
// 结果是 false!
// 虽然两栋房子外观内容一模一样,但它们物理上是两栋不同的房子。
System.out.println(s1 == s2);
// 结果是 true!
// .equals() 不管钥匙/地址,直接对比两栋房子里的内容,发现都是 "hello",即判定相等。
System.out.println(s1.equals(s2));

2.3 字符串常量池机制
在实际开发中,如果不使用 new 关键字,而是直接赋值字符串,会触发 Java 的底层优化机制------字符串常量池.
java
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2); // 结果:true
System.out.println(str1.equals(str2)); // 结果:true
⚠️ 为什么此时
==变成了true?出于节省内存的考虑,当你写
str1 = "hello"时,Java 会在常量池里建一栋 "hello" 的房子。当你再写
str2 = "hello"时,Java 发现池子里已经有同样的房子了,就不会新建,而是把同一把钥匙(物理地址)同时交给了str1和str2。此时,它们不仅内容相同,物理地址也完全一致。

2.4 引用赋值
当你执行 User u2 = u1; 时,Java 虚拟机(JVM)并没有 在堆内存(Heap)里创建一个新的 User 对象。它仅仅是把 u1 变量里存储的十六进制内存地址 (比如 0x105)复制了一份,存到了 u2 变量里。
java
// 1. 在堆中创建一个对象,u1 得到地址 0x105
User u1 = new User("Li");
// 2. 引用赋值:把 u1 的地址直接传给 u2
User u2 = u1;
// 3. 结果验证
System.out.println("u1 == u2: " + (u1 == u2)); // true (地址完全相同)
System.out.println("u1.equals(u2): " + u1.equals(u2)); // true (内容必然相同)
// 4. 侧面验证:修改 u2 会影响 u1 吗?
u2.setName("Wang");
System.out.println("修改u2后,u1的名字是: " + u1.getName()); // 输出 "Wang"

三、常见问题
2.1 自定义类必须同时重写 equals() 和 hashCode()
否则 HashMap、HashSet 等集合会出错。
java
@Override
public boolean equals(Object o) { ... }
@Override
public int hashCode() { ... }
2.2 Integer 缓存机制(-128~127)
java
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2); // true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); // false
⚠️ 建议:始终使用
.equals()比较对象,除非你明确需要判断地址。
四、常见问题对照表
| 问题 | 原因 | 解决方案 |
|---|---|---|
new String() 时 == 返回 false |
堆内存创建了两个不同对象 | 使用 .equals() 比较内容 |
字面量字符串 == 返回 true |
字符串常量池复用 | 理解常量池机制,对象比较仍推荐 .equals() |
| HashMap 存自定义对象时 key 重复/丢失 | 未重写 equals() 和 hashCode() |
必须同时重写两者 |
Integer 127 和 128 == 结果不同 |
IntegerCache 缓存 -128~127 | 统一使用 .equals() |
自定义类用 == 比较始终 false |
引用类型默认比较地址 | 重写 .equals() |
总结
Java 中 == 与 .equals() 的核心区别如下:
==:比较内存地址(物理层面).equals():比较内容(逻辑层面,默认继承自 Object,重写后按需比较)- 字符串常量池是导致
==结果不同的常见特例 - 自定义类必须同时重写
equals()和hashCode()