java中==和.equals()区别
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 等)
基本类型的值直接存在栈中,因此 == 直接比较其数值是否相等。
1 | int a = 100; |

2.2 比较引用数据类型(对象,如 String, Integer 等)
变量(钥匙)存在栈中,实际对象(房子)存在堆中。此时,== 比较的是这两把钥匙是不是同一把,即它们是否指向堆内存里的同一个物理地址。
1 | // 在堆内存里建了一栋房子叫 "hello",钥匙交给了 s1 |

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

2.4 引用赋值
当你执行 User u2 = u1; 时,Java 虚拟机(JVM)并没有在堆内存(Heap)里创建一个新的 User 对象。它仅仅是把 u1 变量里存储的十六进制内存地址(比如 0x105)复制了一份,存到了 u2 变量里。
1 | // 1. 在堆中创建一个对象,u1 得到地址 0x105 |

三、常见问题
2.1 自定义类必须同时重写 equals() 和 hashCode()
否则 HashMap、HashSet 等集合会出错。
1 |
|
2.2 Integer 缓存机制(-128~127)
1 | Integer i1 = 127; |
⚠️ 建议:始终使用
.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()
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Liker's Blog!