
🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录
- Java核心API之Object类:所有类的根父类
-
- [📝 文章摘要](#📝 文章摘要)
- [一、Object类是什么?核心定位与继承特性 📖](#一、Object类是什么?核心定位与继承特性 📖)
-
- [1.1 Object类的官方定位](#1.1 Object类的官方定位)
- [1.2 核心继承特性](#1.2 核心继承特性)
- [1.3 Object类的核心方法分类](#1.3 Object类的核心方法分类)
- [二、Object类常用核心方法拆解:功能+用法+重写 📦](#二、Object类常用核心方法拆解:功能+用法+重写 📦)
-
- [2.1 `toString()`:对象的字符串描述方法](#2.1
toString():对象的字符串描述方法) - [2.2 `equals(Object obj)`:对象内容相等性判断](#2.2
equals(Object obj):对象内容相等性判断) - [2.3 `hashCode()`:对象的哈希码值获取方法](#2.3
hashCode():对象的哈希码值获取方法) - [2.4 `getClass()`:获取对象的运行时类对象](#2.4
getClass():获取对象的运行时类对象) - [2.5 `clone()`:对象的克隆方法](#2.5
clone():对象的克隆方法) - [2.6 `finalize()`:垃圾回收前的清理方法(已废弃)](#2.6
finalize():垃圾回收前的清理方法(已废弃)) - [2.7 `wait()`/`notify()`/`notifyAll()`:线程同步方法](#2.7
wait()/notify()/notifyAll():线程同步方法)
- [2.1 `toString()`:对象的字符串描述方法](#2.1
- [三、Object类方法重写的开发规范 🌟](#三、Object类方法重写的开发规范 🌟)
-
- [3.1 必重写的方法:`toString()`](#3.1 必重写的方法:
toString()) - [3.2 必重写的方法:`equals()` + `hashCode()`](#3.2 必重写的方法:
equals()+hashCode()) - [3.3 按需重写的方法:`clone()`](#3.3 按需重写的方法:
clone()) - [3.4 无需重写的方法:`getClass()`、`wait()`、`notify()`等](#3.4 无需重写的方法:
getClass()、wait()、notify()等) - [3.5 IDE自动生成重写方法(推荐)](#3.5 IDE自动生成重写方法(推荐))
- [3.1 必重写的方法:`toString()`](#3.1 必重写的方法:
- [四、高频误区&避坑指南 ⚠️](#四、高频误区&避坑指南 ⚠️)
- [五、实战:自定义类的Object方法完整重写 🕵️](#五、实战:自定义类的Object方法完整重写 🕵️)
- [✍️ 写在最后](#✍️ 写在最后)

Java核心API之Object类:所有类的根父类
✨ 知识回顾
上一篇我们系统学习了Java的内部类与匿名内部类,掌握了成员内部类、静态内部类、局部内部类、匿名内部类四大类型的语法规则、访问特性和实战场景,还理清了各类内部类的核心区别与避坑要点,完善了Java面向对象的类结构知识。从本篇开始,我们正式进入Java常用核心API 的学习,而所有API的基础,就是Java中所有类的根父类------Object类。它是Java类继承体系的顶端,所有类都直接或间接继承自Object,其提供的11个核心方法是Java开发的基础。这篇我们将从Object类的定位、核心方法入手,逐一讲解常用方法的功能、重写规则与实战应用,帮你吃透这个最基础也最核心的类🚀!
📝 文章摘要
- 核心摘要 :本文讲解Object类的核心定位与继承特性,拆解
equals()、hashCode()、toString()、getClass()等8个常用核心方法的功能、底层逻辑与重写规则,结合实战案例展示方法的实际应用,同时梳理equals()与==的核心区别、equals()和hashCode()的重写约定,让你掌握Object类方法的正确使用与重写技巧,夯实Java API的基础。 - 阅读时长:10分钟
- 适合人群&阅读重点
🎯 Java初学者:重点牢记Object类的常用方法功能,掌握toString()、equals()的基础重写,理解==与equals()的区别。
📚 高校计算机专业学生:理清Object类的设计初衷,理解hashCode()与equals()的底层关联,掌握基于哈希表的集合(HashMap/HashSet)与这两个方法的关系。
💻 初级开发工程师:规范重写自定义类的equals()、hashCode()、toString()方法,避免在集合使用中出现逻辑错误,提升代码的规范性。
📖 面试备考者:熟记Object类的核心方法、==与equals()的区别、equals()和hashCode()的重写约定,应对"Object类有哪些方法""为什么要重写hashCode()"类高频面试题。
一、Object类是什么?核心定位与继承特性 📖
1.1 Object类的官方定位
java.lang.Object是Java所有类的根父类 ,是Java类继承体系的顶端,不存在任何父类。Java中所有的类,无论是系统内置类(String、Integer、ArrayList)还是自定义类,都直接或间接继承自Object类。
1.2 核心继承特性
-
隐式继承 :自定义类时若未显式声明
extends 父类,则默认继承Object类,这是Java编译器的隐式行为。java// 自定义类:隐式继承Object public class Person {} // 等价于显式继承 public class Person extends Object {} -
方法继承 :所有类都会继承Object类的11个核心方法,可直接调用或根据业务需求重写 (部分方法被
final修饰,无法重写,如getClass()、notify())。 -
无参构造 :Object类仅提供一个无参构造方法 ,这也是为什么子类构造方法默认会通过
super()调用父类无参构造的底层原因(最终都会追溯到Object的无参构造)。
1.3 Object类的核心方法分类
Object类共提供11个方法,按功能可分为对象判断/描述 、线程同步 、垃圾回收 三大类,其中对象判断/描述的方法是开发中最常用的,也是面试的核心考点,线程同步和垃圾回收相关方法会在后续多线程、JVM章节讲解。
| 方法分类 | 常用核心方法 | 方法特性 |
|---|---|---|
| 对象判断/描述 | toString()、equals()、hashCode()、getClass()、clone() |
可重写(getClass()除外) |
| 线程同步 | notify()、notifyAll()、wait()(重载3个) |
final修饰,无法重写 |
| 垃圾回收 | finalize() |
已废弃(JDK9+),不推荐使用 |
💡 开发重点:本篇核心讲解对象判断/描述 的8个常用方法(含clone()、toString()、equals()等),这是日常开发和面试的核心。
二、Object类常用核心方法拆解:功能+用法+重写 📦
我们按开发使用频率 从高到低,逐一讲解toString()、equals()、hashCode()、getClass()等核心方法,包括方法功能、默认实现、重写规则与实战案例,让你掌握每个方法的核心用法。
2.1 toString():对象的字符串描述方法
方法功能
返回该对象的字符串表示形式,用于快速获取对象的直观描述,方便打印、调试和日志输出。
方法签名
java
public String toString()
默认实现逻辑
Object类的默认实现返回类的全限定名 + @ + 对象的哈希码十六进制值 ,格式为:全限定类名@哈希码,该默认值无实际业务意义,仅能区分对象,无法体现对象的属性信息。
java
// 自定义Person类,未重写toString()
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 测试默认toString()
public class TestToString {
public static void main(String[] args) {
Person p = new Person("张三", 20);
System.out.println(p); // 等价于System.out.println(p.toString())
}
}
// 输出:com.test.Person@1b6d3586
重写规则与实战
重写原则 :返回包含对象核心属性 的字符串,格式清晰、可读性强,便于调试和日志查看。
实战案例 :重写Person类的toString()方法,返回姓名和年龄属性:
java
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 测试输出:Person{name='张三', age=20}
💡 开发技巧:IDE(IDEA/Eclipse)可自动生成 toString()方法,无需手动编写,IDEA快捷键:Alt+Insert → 选择toString()。
2.2 equals(Object obj):对象内容相等性判断
方法功能
判断当前对象 与传入对象 的内容是否相等 ,返回boolean值,是对象相等性判断的核心方法。
方法签名
java
public boolean equals(Object obj)
默认实现逻辑
Object类的默认实现直接使用==运算符 ,判断两个对象的内存地址是否相等 (即是否为同一个对象),默认的equals()等价于==。
java
// Object类默认equals()实现
public boolean equals(Object obj) {
return (this == obj);
}
核心问题:==与equals()的区别(必记)
这是Java高频面试题,两者的核心区别在于判断的维度不同:
| 对比维度 | ==运算符 |
equals()方法 |
|---|---|---|
| 作用范围 | 可判断基本数据类型+引用数据类型 | 仅能判断引用数据类型(对象) |
| 基本类型判断 | 判断值是否相等 | 无法使用(基本类型无方法) |
| 引用类型判断 | 判断内存地址是否相等(是否为同一个对象) | 默认判断内存地址,重写后可判断内容是否相等 |
| 可修改性 | 运算符,无法重写/修改 | 方法,可根据业务重写逻辑 |
实战案例 :==与equals()的对比测试
java
// 自定义Person类,未重写equals()
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 测试类
public class TestEquals {
public static void main(String[] args) {
// 基本类型:==判断值相等
int a = 10;
int b = 10;
System.out.println(a == b); // true
// 引用类型:两个对象,内存地址不同
Person p1 = new Person("张三", 20);
Person p2 = new Person("张三", 20);
System.out.println(p1 == p2); // false(地址不同)
System.out.println(p1.equals(p2)); // false(默认==,地址不同)
// String类:已重写equals(),判断内容相等
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2); // false(地址不同)
System.out.println(s1.equals(s2)); // true(内容相同)
}
}
重写规则与实战
重写原则 :根据业务唯一标识 判断对象内容是否相等(如Person的姓名+年龄、User的id),需满足自反性、对称性、传递性、一致性 (Java官方约定)。
实战案例 :重写Person类的equals()方法,按姓名+年龄判断相等性:
java
@Override
public boolean equals(Object o) {
// 1. 自反性:对象与自身相等
if (this == o) return true;
// 2. 排除null和类型不同的情况
if (o == null || getClass() != o.getClass()) return false;
// 3. 强制类型转换
Person person = (Person) o;
// 4. 按业务属性判断相等(基本类型用==,引用类型用equals())
return age == person.age && Objects.equals(name, person.name);
}
💡 开发技巧:
- 使用
Objects.equals(a, b)替代a.equals(b),可避免a为null时的NullPointerException; - IDE可自动生成规范的
equals()方法,IDEA快捷键:Alt+Insert→ 选择equals() and hashCode()。
2.3 hashCode():对象的哈希码值获取方法
方法功能
返回该对象的哈希码值(int类型) ,哈希码是通过哈希算法将对象的内存地址/属性转换的整数,用于快速查找(如HashMap、HashSet等哈希表集合的底层实现)。
方法签名
java
public native int hashCode();
💡 关键字native:表示该方法是本地方法,由C/C++实现,底层调用操作系统的API,无Java代码实现。
默认实现逻辑
Object类的默认实现根据对象的内存地址生成哈希码值,不同内存地址的对象,哈希码值一般不同(存在哈希碰撞的可能)。
核心约定:equals()与hashCode()的重写绑定(必记)
这是Java官方强制约定,也是面试高频考点,重写equals()时必须同时重写hashCode(),否则会导致基于哈希表的集合(HashMap/HashSet)出现逻辑错误,核心约定为:
- 相等的对象,哈希码必须相等 :如果
a.equals(b) == true,则a.hashCode()必须等于b.hashCode(); - 哈希码相等的对象,不一定相等 :如果
a.hashCode() == b.hashCode(),则a.equals(b)可能为false(哈希碰撞); - 不相等的对象,哈希码可以相等:允许哈希碰撞,这是哈希算法的正常现象。
重写规则与实战
重写原则 :与equals()的判断逻辑保持一致 ,equals()中用到的属性,都要参与哈希码的计算。
实战案例 :重写Person类的hashCode()方法,与equals()一致,按姓名+年龄计算:
java
@Override
public int hashCode() {
// 借助Objects.hash(),传入参与判断的属性,自动生成哈希码
return Objects.hash(name, age);
}
💡 开发技巧:
- 无需手动编写哈希算法,使用
Objects.hash(属性1, 属性2,...)可快速生成规范的哈希码,支持多属性参与计算; - IDE自动生成
equals()时,会同步生成对应的hashCode(),保证两者逻辑一致。
核心作用:支撑哈希表集合的底层实现
哈希表集合(HashMap/HashSet)的底层是数组+链表/红黑树,其核心查找逻辑分为两步:
- 根据对象的
hashCode()计算数组下标,快速定位到数组的某个位置; - 通过
equals()判断该位置的元素与目标元素是否相等,解决哈希碰撞。
若仅重写equals()不重写hashCode(),会导致两个内容相等的对象哈希码不同,在HashSet中会被当作两个不同对象存储,违背集合的去重逻辑。
2.4 getClass():获取对象的运行时类对象
方法功能
返回该对象的运行时类的Class对象(反射的核心对象),该方法能获取对象实际的类类型(而非引用类型),是多态场景下获取实际对象类型的核心方法。
方法签名
java
public final Class<?> getClass()
💡 关键字final:该方法无法被重写,保证获取的类对象是真实的运行时类型。
实战案例:多态场景下获取实际对象类型
java
// 抽象父类
public abstract class Animal {}
// 子类Cat
public class Cat extends Animal {}
// 子类Dog
public class Dog extends Animal {}
// 测试类
public class TestGetClass {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Animal a1 = new Cat();
Animal a2 = new Dog();
// 获取运行时类对象,打印类的全限定名
System.out.println(a1.getClass().getName()); // com.test.Cat
System.out.println(a2.getClass().getName()); // com.test.Dog
// 判断两个对象的实际类型是否相同
System.out.println(a1.getClass() == a2.getClass()); // false
}
}
核心应用:反射开发
getClass()获取的Class对象是Java反射的入口,通过该对象可获取类的属性、方法、构造方法,实现动态创建对象、调用方法等反射操作,是框架开发(Spring、MyBatis)的核心基础。
2.5 clone():对象的克隆方法
方法功能
创建并返回当前对象的副本 ,实现对象的复制,分为浅克隆 和深克隆(后续会详细讲解)。
方法签名
java
protected native Object clone() throws CloneNotSupportedException;
核心使用条件
- 自定义类要实现**
Cloneable接口**(标记接口,无任何方法),否则调用clone()会抛出CloneNotSupportedException; - 需重写
clone()方法,并将访问权限改为public(父类中是protected,外部无法直接调用)。
实战案例:实现对象的浅克隆
java
// 实现Cloneable接口,标记可克隆
public class Person implements Cloneable {
private String name;
private int age;
// 构造方法、getter/setter、toString()省略
// 重写clone(),改为public,处理异常
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 调用父类Object的clone()
}
}
// 测试类
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person("张三", 20);
Person p2 = (Person) p1.clone(); // 克隆对象
System.out.println(p1); // Person{name='张三', age=20}
System.out.println(p2); // Person{name='张三', age=20}
System.out.println(p1 == p2); // false(不同对象,内存地址不同)
}
}
核心区别:浅克隆vs深克隆
- 浅克隆 :默认的
clone()实现是浅克隆,仅复制对象的基本类型属性,引用类型属性仅复制地址(原对象和副本共享引用类型对象); - 深克隆 :不仅复制基本类型属性,还会递归复制引用类型属性,原对象和副本的引用类型属性是独立的对象,无共享。
💡 注意:Cloneable接口是标记接口,仅用于标记该类可被克隆,无任何抽象方法,这是Java中标记接口的典型应用。
2.6 finalize():垃圾回收前的清理方法(已废弃)
方法功能
当对象被JVM的垃圾回收器判定为无引用可达时,在垃圾回收前会调用该方法,用于做对象的最后清理工作(如释放资源)。
方法签名
java
protected void finalize() throws Throwable
核心说明
该方法在JDK9中被标记为废弃,JDK17中被彻底移除,原因是:
- 执行时机不确定:垃圾回收的时机由JVM决定,
finalize()的执行时间无法预测; - 性能损耗大:会增加垃圾回收的复杂度,降低JVM的回收效率;
- 替代方案:推荐使用try-with-resources(资源自动关闭)或手动编写关闭方法,实现资源的释放。
2.7 wait()/notify()/notifyAll():线程同步方法
方法功能
这三个方法是线程间通信的核心方法,用于实现多线程的等待/唤醒机制,解决线程同步问题。
方法签名
java
public final void wait() throws InterruptedException
public final void notify()
public final void notifyAll()
核心特性
- 均被
final修饰,无法被重写; - 必须在同步代码块/同步方法 中调用(持有对象的锁),否则会抛出
IllegalMonitorStateException; - 属于线程同步的核心方法,会在后续多线程专题中详细讲解,此处仅做认知。
三、Object类方法重写的开发规范 🌟
在自定义类中,重写Object类的方法是日常开发的基本操作,遵循以下规范可保证代码的规范性、正确性,避免逻辑错误:
3.1 必重写的方法:toString()
所有自定义业务类都建议重写,默认的全限定类名+哈希码无实际业务意义,重写后能快速查看对象的属性信息,提升调试和日志输出的效率。
3.2 必重写的方法:equals() + hashCode()
当需要判断对象内容相等,或该类的对象会存入哈希表集合(HashMap/HashSet/HashTable)时,必须同时重写,且两者逻辑保持一致,遵循Java官方的约定。
3.3 按需重写的方法:clone()
仅当需要实现对象克隆时重写,且必须实现Cloneable接口,根据业务需求选择浅克隆 或深克隆。
3.4 无需重写的方法:getClass()、wait()、notify()等
被final修饰的方法无法重写,无需做任何操作,直接调用即可。
3.5 IDE自动生成重写方法(推荐)
开发中无需手动编写重写代码,IDE(IDEA/Eclipse)可自动生成规范的toString()、equals()、hashCode()方法,保证逻辑的正确性和规范性。
IDEA快捷键 :Alt+Insert → 选择对应方法即可(如toString()、equals() and hashCode())。
四、高频误区&避坑指南 ⚠️
误区1:重写equals()时未重写hashCode()
❌ 错误后果:两个内容相等的对象哈希码不同,存入HashSet时无法去重,存入HashMap时无法正确查找;
✅ 正确做法:重写equals()必须同时重写hashCode(),且两者使用相同的属性参与判断/计算。
误区2:用equals()判断基本数据类型,或用==判断引用类型的内容
❌ 错误示例:
java
int a = 10;
int b = 10;
System.out.println(a.equals(b)); // 报错:基本类型无方法
String s1 = "Java";
String s2 = new String("Java");
System.out.println(s1 == s2); // false,错误判断内容
✅ 正确做法:基本类型用==判断值,引用类型用equals()判断内容(重写后)。
误区3:在equals()中直接强制类型转换,未做类型判断
❌ 错误示例:
java
@Override
public boolean equals(Object o) {
Person p = (Person) o; // 若o不是Person类型,抛出ClassCastException
return age == p.age && Objects.equals(name, p.name);
}
✅ 正确做法:先通过o == null || getClass() != o.getClass()判断类型,再强制转换。
误区4:认为hashCode()返回的是对象的内存地址
❌ 错误认知:Object类的hashCode()返回的是内存地址;
✅ 正确结论:Object类的hashCode()是基于内存地址生成的哈希码,并非直接返回内存地址(内存地址是十六进制,哈希码是int类型),且重写后哈希码与内存地址无关。
误区5:调用clone()时未实现Cloneable接口
❌ 错误后果:抛出CloneNotSupportedException;
✅ 正确做法:自定义类实现Cloneable标记接口,并重写clone()方法,将访问权限改为public。
五、实战:自定义类的Object方法完整重写 🕵️
业务场景:定义User类,包含id(唯一标识)、username、password属性,实现toString()、equals()、hashCode()的规范重写,支持对象内容相等判断和哈希表集合存储。
完整代码实现
java
import java.util.Objects;
// 自定义User类,隐式继承Object
public class User {
// 业务属性:id为唯一标识
private Long id;
private String username;
private String password;
// 构造方法
public User(Long id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
// getter/setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
// 重写toString():包含所有核心属性
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
// 重写equals():按id(唯一标识)判断相等性
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
// 重写hashCode():与equals一致,仅用id参与计算
@Override
public int hashCode() {
return Objects.hash(id);
}
// 测试主方法
public static void main(String[] args) {
User u1 = new User(1L, "zhangsan", "123456");
User u2 = new User(1L, "zhangsan_1", "654321");
User u3 = new User(2L, "lisi", "123456");
// 测试toString()
System.out.println(u1); // User{id=1, username='zhangsan', password='123456'}
// 测试equals():id相同则相等
System.out.println(u1.equals(u2)); // true
System.out.println(u1.equals(u3)); // false
// 测试hashCode():相等的对象哈希码相同
System.out.println(u1.hashCode() == u2.hashCode()); // true
System.out.println(u1.hashCode() == u3.hashCode()); // false
}
}
代码亮点 ✨
equals()按唯一标识id判断相等性,符合业务逻辑(同一个用户的id唯一);hashCode()与equals()逻辑一致,仅用id参与计算,保证相等的对象哈希码相同;- 用
Objects.equals()和Objects.hash()避免空指针异常,保证代码的健壮性; - 重写的
toString()包含所有核心属性,便于调试和日志输出。
✍️ 写在最后
- Object类是Java所有类的根父类,所有类都隐式继承自它,其提供的核心方法是Java开发的基础;
- 开发中最常用的方法为
toString()、equals()、hashCode()、getClass(),其中equals()与hashCode()必须绑定重写,遵循Java官方约定; ==与equals()的核心区别:==判断基本类型值/引用类型地址,equals()默认判断地址,重写后判断内容;- 开发规范:所有自定义业务类建议重写
toString(),需要判断内容相等或存入哈希表集合时,必须同时重写equals()和hashCode(),推荐使用IDE自动生成,保证逻辑规范。
Object类作为Java核心API的基础,是后续所有类学习的前提。下一篇我们将学习Java字符串相关API (String、StringBuffer、StringBuilder),讲解字符串的特性、常用方法与三者的核心区别,掌握Java中字符串的正确使用技巧💪!
❤️ 我是黎雁,专注Java基础与实战分享,关注我,一起从0到1吃透Java!
📚 后续文章预告:《Java字符串API:String/StringBuffer/StringBuilder详解》
💬 评论区交流:你在重写equals()和hashCode()时遇到过哪些问题?或者对Object类的native方法有哪些疑惑,欢迎留言讨论~