目录
[1. ==](#1. ==)
[2. equals()](#2. equals())
[3. hashCode()](#3. hashCode())
[1. == 底层原理](#1. == 底层原理)
[2. equals() 底层原理(原生Object源码)](#2. equals() 底层原理(原生Object源码))
[3. hashCode() 底层原理](#3. hashCode() 底层原理)
[1. 什么时候必须重写 equals()](#1. 什么时候必须重写 equals())
[2. 什么时候必须重写 hashCode()](#2. 什么时候必须重写 hashCode())
[3. 什么时候不用重写](#3. 什么时候不用重写)
[4. 补充:String/JDK常用类现状](#4. 补充:String/JDK常用类现状)
一、核心定义&存在意义&基础作用
1. ==
是什么
Java原生运算符,语法级比较符号,不依赖任何方法实现。
为什么存在
JVM需要一套底层原生机制,直接判定两个变量 内存指向/基础值是否完全一致。
作用
- 比较基本数据类型 :判断栈中存储的字面数值是否相等;
- 比较引用数据类型:判断两个引用变量**堆内存地址(对象指针)**是否指向同一个对象。
2. equals()
是什么
Object类中定义的 public boolean equals(Object obj) 实例方法,所有类默认继承。
为什么存在
==仅能比内存地址,业务中需要 比对对象内容/属性逻辑相等,而非同一个内存对象,因此提供可自定义的逻辑比较入口。
作用
实现 业务层面的对象内容相等判定,开发者可重写该方法,自定义相等规则。
3. hashCode()
是什么
Object类中定义的 public native int hashCode() 本地方法,返回对象int类型哈希码。
为什么存在
配合哈希表集合(HashMap/HashSet/HashTable)实现 高效存取、快速判重;哈希表先靠哈希码定位桶,再用equals精准比对,降低比对开销。
作用
- 为对象生成固定哈希编码,确定哈希表存储桶位置;
- 约束规范:两个equals=true的对象,必须哈希码相同;减少equals全量比对次数。
二、底层原理(原生源码+JVM层级)
1. == 底层原理
- 基本类型(byte/short/int/long/float/double/char/boolean):
变量直接在虚拟机栈存 真实数值,
==直接比对栈内两个数值的二进制数据,完全一致则返回true。 - 引用类型:
变量在栈中存 对象堆内存的地址指针,==直接比对两个指针的内存地址值;
只有指向堆中同一个对象,才返回true。
无任何方法调用,纯JVM指令级地址/数值比对。
2. equals() 底层原理(原生Object源码)
Object类原生源码:
public boolean equals(Object obj) { return (this == obj); }
底层原生实现: 直接复用 == 地址比对。
- 未重写时:equals和==比对逻辑完全一致,只比内存地址;
- 重写后:开发者自定义属性比对逻辑,不再依赖地址比对。
3. hashCode() 底层原理
- 原生是native本地方法,由JVM底层实现,非Java代码编写;
- JVM默认生成规则:基于对象堆内存地址、对象头哈希字段、随机数、对象生命周期生成int整数;
- 原生规则:
- 同一个对象,多次调用hashCode,返回值始终一致;
- 不同对象,大概率哈希码不同(存在哈希碰撞);
4.哈希表工作底层:
存元素:计算hashCode→取模定位哈希桶→桶内用equals逐个比对;
判重:先比hashCode,不同直接判不相等;相同再执行equals精准校验。
三、三者联动规范(强制底层契约)
- 若a.equals(b) = true → 必须保证 a.hashCode() == b.hashCode();
- 若a.hashCode() != b.hashCode() → 必须保证 a.equals(b) = false;
- 若a.hashCode() == b.hashCode() → a.equals(b)可true可false(哈希碰撞);
- 只重写equals不重写hashCode:违反契约,HashMap/HashSet会出现逻辑异常(相同内容对象存重复、取不出)。
四、重写场景(精准业务落地)
1. 什么时候必须重写 equals()
- 自定义实体类(User/Order/Student等),需要按属性判定对象逻辑相等,而非地址相等;
- 对象需要存入HashSet、作为HashMap的key,必须重写equals做内容精准判重;
- 业务需求:两个对象所有核心字段一致即判定为同一个业务数据。
2. 什么时候必须重写 hashCode()
只要重写了equals(),就必须同步重写hashCode();
专属场景:对象要放入所有哈希结构集合(HashMap、HashSet、LinkedHashMap等),不重写会导致:
- 内容相等的两个对象,hashCode不同,存到不同哈希桶;
- 集合判重失效、get(key)获取不到数据、去重失败。
3. 什么时候不用重写
- 实体类仅做数据传输,无需比对相等、不存入哈希集合;
- 枚举类、基础包装类(Integer/String已JDK底层重写完毕,直接用);
- 只靠==地址比对就能满足所有业务逻辑。
4. 补充:String/JDK常用类现状
String、Integer、Long、BigDecimal等JDK类:
已原生重写equals(比字符/数值内容)+ 重写hashCode(按内容生成哈希码),直接使用即可满足内容相等比对。