Java核心API之Object类:所有类的根父类

🏠个人主页:黎雁

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyXJAVA游戏规划程序人生

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

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中所有的类,无论是系统内置类(StringIntegerArrayList)还是自定义类,都直接或间接继承自Object类。

1.2 核心继承特性

  1. 隐式继承 :自定义类时若未显式声明extends 父类,则默认继承Object类,这是Java编译器的隐式行为。

    java 复制代码
    // 自定义类:隐式继承Object
    public class Person {}
    // 等价于显式继承
    public class Person extends Object {}
  2. 方法继承 :所有类都会继承Object类的11个核心方法,可直接调用或根据业务需求重写 (部分方法被final修饰,无法重写,如getClass()notify())。

  3. 无参构造 :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);
}

💡 开发技巧:

  1. 使用Objects.equals(a, b)替代a.equals(b),可避免anull时的NullPointerException
  2. 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)出现逻辑错误,核心约定为:

  1. 相等的对象,哈希码必须相等 :如果a.equals(b) == true,则a.hashCode()必须等于b.hashCode()
  2. 哈希码相等的对象,不一定相等 :如果a.hashCode() == b.hashCode(),则a.equals(b)可能为false(哈希碰撞);
  3. 不相等的对象,哈希码可以相等:允许哈希碰撞,这是哈希算法的正常现象。
重写规则与实战

重写原则 :与equals()的判断逻辑保持一致equals()中用到的属性,都要参与哈希码的计算。
实战案例 :重写Person类的hashCode()方法,与equals()一致,按姓名+年龄计算:

java 复制代码
@Override
public int hashCode() {
    // 借助Objects.hash(),传入参与判断的属性,自动生成哈希码
    return Objects.hash(name, age);
}

💡 开发技巧:

  1. 无需手动编写哈希算法,使用Objects.hash(属性1, 属性2,...)可快速生成规范的哈希码,支持多属性参与计算;
  2. IDE自动生成equals()时,会同步生成对应的hashCode(),保证两者逻辑一致。
核心作用:支撑哈希表集合的底层实现

哈希表集合(HashMap/HashSet)的底层是数组+链表/红黑树,其核心查找逻辑分为两步:

  1. 根据对象的hashCode()计算数组下标,快速定位到数组的某个位置;
  2. 通过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;
核心使用条件
  1. 自定义类要实现**Cloneable接口**(标记接口,无任何方法),否则调用clone()会抛出CloneNotSupportedException
  2. 需重写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深克隆
  1. 浅克隆 :默认的clone()实现是浅克隆,仅复制对象的基本类型属性,引用类型属性仅复制地址(原对象和副本共享引用类型对象);
  2. 深克隆 :不仅复制基本类型属性,还会递归复制引用类型属性,原对象和副本的引用类型属性是独立的对象,无共享。
    💡 注意:Cloneable接口是标记接口,仅用于标记该类可被克隆,无任何抽象方法,这是Java中标记接口的典型应用。

2.6 finalize():垃圾回收前的清理方法(已废弃)

方法功能

当对象被JVM的垃圾回收器判定为无引用可达时,在垃圾回收前会调用该方法,用于做对象的最后清理工作(如释放资源)。

方法签名
java 复制代码
protected void finalize() throws Throwable
核心说明

该方法在JDK9中被标记为废弃,JDK17中被彻底移除,原因是:

  1. 执行时机不确定:垃圾回收的时机由JVM决定,finalize()的执行时间无法预测;
  2. 性能损耗大:会增加垃圾回收的复杂度,降低JVM的回收效率;
  3. 替代方案:推荐使用try-with-resources(资源自动关闭)或手动编写关闭方法,实现资源的释放。

2.7 wait()/notify()/notifyAll():线程同步方法

方法功能

这三个方法是线程间通信的核心方法,用于实现多线程的等待/唤醒机制,解决线程同步问题。

方法签名
java 复制代码
public final void wait() throws InterruptedException
public final void notify()
public final void notifyAll()
核心特性
  1. 均被final修饰,无法被重写
  2. 必须在同步代码块/同步方法 中调用(持有对象的锁),否则会抛出IllegalMonitorStateException
  3. 属于线程同步的核心方法,会在后续多线程专题中详细讲解,此处仅做认知。

三、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
    }
}

代码亮点 ✨

  1. equals()唯一标识id判断相等性,符合业务逻辑(同一个用户的id唯一);
  2. hashCode()equals()逻辑一致,仅用id参与计算,保证相等的对象哈希码相同;
  3. Objects.equals()Objects.hash()避免空指针异常,保证代码的健壮性;
  4. 重写的toString()包含所有核心属性,便于调试和日志输出。

✍️ 写在最后

  1. Object类是Java所有类的根父类,所有类都隐式继承自它,其提供的核心方法是Java开发的基础;
  2. 开发中最常用的方法为toString()equals()hashCode()getClass(),其中equals()hashCode()必须绑定重写,遵循Java官方约定;
  3. ==equals()的核心区别:==判断基本类型值/引用类型地址,equals()默认判断地址,重写后判断内容;
  4. 开发规范:所有自定义业务类建议重写toString(),需要判断内容相等或存入哈希表集合时,必须同时重写equals()hashCode(),推荐使用IDE自动生成,保证逻辑规范。

Object类作为Java核心API的基础,是后续所有类学习的前提。下一篇我们将学习Java字符串相关APIStringStringBufferStringBuilder),讲解字符串的特性、常用方法与三者的核心区别,掌握Java中字符串的正确使用技巧💪!


❤️ 我是黎雁,专注Java基础与实战分享,关注我,一起从0到1吃透Java!

📚 后续文章预告:《Java字符串API:String/StringBuffer/StringBuilder详解》

💬 评论区交流:你在重写equals()和hashCode()时遇到过哪些问题?或者对Object类的native方法有哪些疑惑,欢迎留言讨论~

相关推荐
Remember_9931 小时前
【LeetCode精选算法】位运算专题
java·开发语言·jvm·后端·算法·leetcode
工程师老罗1 小时前
Pytorch自定义数据集的用法
开发语言·pytorch·python
OnYoung1 小时前
设计模式在C++中的实现
开发语言·c++·算法
曹牧1 小时前
Java:代理转发配置Nginx
java·开发语言·nginx
foundbug9991 小时前
利用MATLAB计算梁单元刚度矩阵并组装成总体刚度矩阵
开发语言·matlab·矩阵
洋不写bug2 小时前
JavaEE基础,计算机是如何工作的
java·java-ee·状态模式
码农水水2 小时前
小红书Java面试被问:mTLS(双向TLS)的证书验证和握手过程
java·开发语言·数据库·redis·python·面试·开源
zmzb01032 小时前
C++课后习题训练记录Day85
开发语言·c++·算法
梵刹古音2 小时前
【C语言】 整型变量
c语言·开发语言