news 2026/5/8 17:20:57

Java Object 类常用方法详解:equals、hashCode、toString

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Object 类常用方法详解:equals、hashCode、toString

前面学完抽象类、接口、内部类之后,接下来非常值得继续掌握的一个知识点,就是Object类中的几个核心方法:

  • equals()
  • hashCode()
  • toString()

这三个方法在 Java 开发中出现频率非常高。

可以这么说:

  • 不理解equals(),你就容易在“对象比较”上出错;
  • 不理解hashCode(),你就很难真正弄明白HashMapHashSet
  • 不理解toString(),你打印对象时看到的内容就总是一串看不懂的地址值风格字符串。

所以这部分内容,不只是面试常考,在真实开发里也非常常用。


一、Object 类是什么?

Object是 Java 中所有类的根类。

也就是说,任何一个类,如果没有明确继承其他类,那么默认都会继承Object

classStudent{}

上面这个类,其实等价于:

classStudentextendsObject{}

因此,任何 Java 对象,天然都可以使用Object类中的方法,比如:

  • equals()
  • hashCode()
  • toString()
  • getClass()
  • wait()
  • notify()

其中最常用、最基础的,就是今天要讲的这三个。


二、equals() 方法详解

1. equals() 是干什么的?

equals()用来比较两个对象是否“相等”。

但是这里的“相等”,到底比较什么,要分情况看。

先看代码:

Strings1=newString("abc");Strings2=newString("abc");System.out.println(s1==s2);// falseSystem.out.println(s1.equals(s2));// true

这里就说明:

  • ==比较的是两个变量保存的地址是否相同
  • equals()比较的是两个对象的内容是否相同(前提是类重写了 equals 方法)

2. == 和 equals() 的区别

这是初学阶段最容易混淆的地方。

(1)对于基本数据类型

==比较的是值。

inta=10;intb=10;System.out.println(a==b);// true
(2)对于引用数据类型

==比较的是地址值。

Strings1=newString("hello");Strings2=newString("hello");System.out.println(s1==s2);// false

因为s1s2指向的是两个不同对象。

(3)equals() 默认也是比较地址

很多同学以为equals()天生就是比较内容,其实不是。

Object类中默认的equals()实现,本质上还是比较地址。

你可以简单理解为:

publicbooleanequals(Objectobj){return(this==obj);}

也就是说,如果一个类没有重写equals(),那么它的效果和==差不多。


3. 为什么很多类的 equals() 比较内容?

因为像StringInteger这些类,已经重写了equals()方法。

例如String重写后,比较的是字符串内容。

Strings1=newString("java");Strings2=newString("java");System.out.println(s1.equals(s2));// true

这就是因为String类把“两个字符串内容相同”定义为了相等。


4. 自定义类为什么要重写 equals()?

来看一个例子:

classStudent{Stringname;intage;publicStudent(Stringname,intage){this.name=name;this.age=age;}}publicclassTest{publicstaticvoidmain(String[]args){Students1=newStudent("张三",18);Students2=newStudent("张三",18);System.out.println(s1.equals(s2));}}

输出结果是:

false

为什么?

因为Student没有重写equals(),所以调用的是Object的实现,本质比较的是地址。

但从业务角度看:

  • 姓名相同
  • 年龄相同

我们通常会认为这两个学生对象“内容相等”。

这时就应该重写equals()


5. equals() 的重写示例

importjava.util.Objects;classStudent{privateStringname;privateintage;publicStudent(Stringname,intage){this.name=name;this.age=age;}@Overridepublicbooleanequals(Objectobj){if(this==obj){returntrue;}if(obj==null||getClass()!=obj.getClass()){returnfalse;}Studentstudent=(Student)obj;returnage==student.age&&Objects.equals(name,student.name);}}

测试:

publicclassTest{publicstaticvoidmain(String[]args){Students1=newStudent("张三",18);Students2=newStudent("张三",18);System.out.println(s1.equals(s2));// true}}

6. equals() 重写时常见写法解释

this == obj

表示如果两个引用本来就指向同一个对象,那肯定相等,直接返回true

obj == null

如果传进来的对象是null,那一定不相等。

getClass() != obj.getClass()

表示只有类型完全相同,才继续比较。

强制类型转换
Studentstudent=(Student)obj;

因为传入参数类型是Object,需要转成当前类后才能访问属性。

比较成员变量
returnage==student.age&&Objects.equals(name,student.name);

这一步才是真正的“内容比较”。


7. equals() 使用时的注意事项

(1)重写 equals() 时,通常也要重写 hashCode()

这是一个非常重要的规则,后面会详细讲。

(2)尽量使用Objects.equals()比较引用类型

因为这样可以避免空指针异常。

Objects.equals(name,student.name)

比下面这种更安全:

name.equals(student.name)

因为如果namenull,后者会报错。


三、hashCode() 方法详解

1. hashCode() 是什么?

hashCode()方法返回的是对象的哈希值,也可以理解为一个整数“编号”。

这个值不是对象地址本身,但通常和对象的存储、查找效率有关。

Objectobj=newObject();System.out.println(obj.hashCode());

每个对象都可以调用这个方法。


2. hashCode() 有什么用?

它最主要的用途是:提高哈希结构中的查找效率。

比如:

  • HashMap
  • HashSet
  • Hashtable

这些集合在存储对象时,并不是一个一个慢慢比较,而是先根据hashCode()找位置,再进一步比较。

也就是说:

  • 先看哈希值
  • 再决定放到哪个位置
  • 如果哈希冲突,再调用equals()进一步比较

所以:

hashCode()主要服务于哈希类集合。


3. equals() 和 hashCode() 的关系

这是最核心的内容之一。

Java 规范要求:

规则 1

如果两个对象通过equals()比较结果为true,那么它们的hashCode()必须相同。

规则 2

如果两个对象的hashCode()相同,equals()不一定为true

也就是说:

  • 相等对象,哈希值必须相等
  • 哈希值相等,对象不一定相等

4. 为什么重写 equals() 后还要重写 hashCode()?

因为如果你只重写了equals(),没有重写hashCode(),那么在哈希集合中可能会出现逻辑错误。

看例子:

importjava.util.HashSet;importjava.util.Objects;classStudent{privateStringname;privateintage;publicStudent(Stringname,intage){this.name=name;this.age=age;}@Overridepublicbooleanequals(Objectobj){if(this==obj){returntrue;}if(obj==null||getClass()!=obj.getClass()){returnfalse;}Studentstudent=(Student)obj;returnage==student.age&&Objects.equals(name,student.name);}}publicclassTest{publicstaticvoidmain(String[]args){HashSet<Student>set=newHashSet<>();set.add(newStudent("张三",18));set.add(newStudent("张三",18));System.out.println(set.size());}}

很多人以为结果会是1,但实际上可能是2

原因就在于:

  • equals()认为这两个对象相等
  • 但它们没有相同的哈希值规则
  • HashSet可能先把它们分到不同位置
  • 于是去重失败

5. hashCode() 的正确重写方式

Java 中通常直接使用Objects.hash(...)

importjava.util.Objects;classStudent{privateStringname;privateintage;publicStudent(Stringname,intage){this.name=name;this.age=age;}@Overridepublicbooleanequals(Objectobj){if(this==obj){returntrue;}if(obj==null||getClass()!=obj.getClass()){returnfalse;}Studentstudent=(Student)obj;returnage==student.age&&Objects.equals(name,student.name);}@OverridepublicinthashCode(){returnObjects.hash(name,age);}}

这样写后:

  • 只要nameage相同
  • equals()返回true
  • hashCode()也会一致

这就满足规范要求了。


6. 一句话理解 hashCode()

你可以把hashCode()理解成:

给对象生成一个“分类编号”,方便哈希集合快速定位。

equals()则是:

当两个对象被分到同一区域后,再进一步精确比较它们是否真的相等。

所以两者经常配合使用。


四、toString() 方法详解

1. toString() 是干什么的?

toString()方法用于返回对象的字符串表示形式。

例如:

Objectobj=newObject();System.out.println(obj.toString());System.out.println(obj);

上面两句输出效果本质是一样的,因为直接打印对象时,Java 会自动调用对象的toString()方法。


2. Object 默认的 toString() 长什么样?

默认情况下,Object类中的toString()返回结果格式大致如下:

类名@十六进制哈希值

比如:

Student@1b6d3586

这个结果对于程序运行没问题,但对人阅读不友好。

所以在开发中,我们通常会重写toString(),让对象打印时直接显示关键属性。


3. 为什么要重写 toString()?

看下面例子:

classStudent{Stringname;intage;publicStudent(Stringname,intage){this.name=name;this.age=age;}}publicclassTest{publicstaticvoidmain(String[]args){Students=newStudent("李四",20);System.out.println(s);}}

输出类似:

Student@6d06d69c

这对调试和日志输出帮助不大。

如果重写toString()

classStudent{Stringname;intage;publicStudent(Stringname,intage){this.name=name;this.age=age;}@OverridepublicStringtoString(){return"Student{name='"+name+"', age="+age+"}";}}

再打印:

Student{name='李四',age=20}

这样就直观多了。


4. toString() 的典型使用场景

(1)打印对象
System.out.println(student);
(2)调试程序

当你想快速看对象内部数据时,重写toString()非常方便。

(3)日志输出

在记录日志时,输出对象信息会更加清晰。


五、三个方法放在一起理解

我们用一个完整例子,把equals()hashCode()toString()串起来。

importjava.util.Objects;classStudent{privateStringname;privateintage;publicStudent(Stringname,intage){this.name=name;this.age=age;}@Overridepublicbooleanequals(Objectobj){if(this==obj){returntrue;}if(obj==null||getClass()!=obj.getClass()){returnfalse;}Studentstudent=(Student)obj;returnage==student.age&&Objects.equals(name,student.name);}@OverridepublicinthashCode(){returnObjects.hash(name,age);}@OverridepublicStringtoString(){return"Student{name='"+name+"', age="+age+"}";}}publicclassTest{publicstaticvoidmain(String[]args){Students1=newStudent("王五",19);Students2=newStudent("王五",19);System.out.println(s1.equals(s2));System.out.println(s1.hashCode());System.out.println(s2.hashCode());System.out.println(s1);}}

输出可能类似:

true1234567812345678Student{name='王五',age=19}

这说明:

  • equals():比较内容
  • hashCode():生成一致的哈希值
  • toString():打印更友好的对象信息

六、初学者最容易踩的坑

1. 把==equals()混为一谈

记住:

  • ==比较地址(引用类型)
  • equals()比较内容(前提是重写了)

2. 只重写 equals(),不重写 hashCode()

这会导致哈希集合中行为异常。

3. 以为 toString() 不重要

实际上它对调试、打印日志、排查问题都很有帮助。

4. equals() 中直接调用属性的 equals(),可能空指针

比如:

name.equals(other.name)

如果namenull,会报错。更推荐:

Objects.equals(name,other.name)

七、面试/考试常见问题总结

1. Object 类中最常用的方法有哪些?

答:equals()hashCode()toString()

2. equals() 和==有什么区别?

答:

  • ==对基本类型比较值,对引用类型比较地址
  • equals()默认比较地址,重写后一般比较内容

3. 为什么重写 equals() 时通常要重写 hashCode()?

答:

因为 Java 规定相等对象必须有相同哈希值,否则在HashSetHashMap等集合中会出现逻辑问题。

4. 为什么要重写 toString()?

答:

为了让对象打印时显示更有意义的内容,方便调试和日志查看。


八、最后总结

学完Object类中的这三个方法后,你要真正记住下面这三句话:

  • equals():判断两个对象内容是否相等
  • hashCode():为对象提供哈希值,方便哈希集合快速定位
  • toString():返回对象的字符串表示,便于输出和调试

再进一步总结就是:

  • equals()决定“逻辑上是否相等”
  • hashCode()决定“哈希结构里怎么快速找”
  • toString()决定“打印出来给人看是什么样子”

如果这三个方法你真正理解了,后面学习集合框架,尤其是HashSetHashMap时,会顺畅很多。


九、练习题

练习 1

定义一个Book类,包含titleprice两个属性,重写equals()toString()

练习 2

继续完善Book类,再重写hashCode(),并放入HashSet中测试是否能正确去重。

练习 3

分别使用==equals()比较两个内容相同的字符串对象,观察输出结果并解释原因。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 17:20:46

HyperRAM:破解紧凑型HMI内存瓶颈的低引脚、低功耗解决方案

1. 项目概述&#xff1a;HMI系统中的内存扩展挑战与机遇在工业控制和消费电子领域&#xff0c;人机界面&#xff08;HMI&#xff09;是我们与机器“对话”的窗口。无论是工厂产线上复杂的SCADA控制面板&#xff0c;还是家里智能温控器上那块小小的触摸屏&#xff0c;其背后都依…

作者头像 李华
网站建设 2026/5/8 17:20:11

Windows 里的环境变量

Windows 里的环境变量大致可以分三层&#xff1a; Process 当前进程级 User 用户级 Machine 系统级可以理解为作用范围从小到大&#xff1a; Process < User < Machine1. Process&#xff1a;当前进程级环境变量 Process 环境变量只在当前程序进程里有效。 例如你在…

作者头像 李华
网站建设 2026/5/8 17:19:51

Python爬虫入门实战:从基础请求到Selenium自动化

一、前言对于爬虫入门者而言&#xff0c;最核心的需求是 “能看懂、能运行、能落地”。本文从零开始&#xff0c;循序渐进讲解 Python 爬虫核心技术 —— 从基础的requests请求、UA 伪装、数据保存&#xff0c;到re正则解析、lxml的 XPath 提取&#xff0c;再到 Selenium 自动化…

作者头像 李华
网站建设 2026/5/8 17:19:16

3步掌握Genshin FPS Unlock:突破60帧限制的完整技术指南

3步掌握Genshin FPS Unlock&#xff1a;突破60帧限制的完整技术指南 【免费下载链接】genshin-fps-unlock unlocks the 60 fps cap 项目地址: https://gitcode.com/gh_mirrors/ge/genshin-fps-unlock Genshin FPS Unlock是一款专为《原神》玩家设计的帧率解锁工具&#…

作者头像 李华
网站建设 2026/5/8 17:18:50

STM32CubeIDE实战:用I2C中断+DMA驱动AHT20温湿度传感器(附完整代码)

STM32CubeIDE实战&#xff1a;用I2C中断DMA驱动AHT20温湿度传感器&#xff08;附完整代码&#xff09; 在嵌入式系统开发中&#xff0c;环境监测是一个常见需求。AHT20作为新一代数字温湿度传感器&#xff0c;以其高精度、低功耗和I2C接口的便利性&#xff0c;成为许多项目的首…

作者头像 李华