Object 类与 equals/hashCode

一、Object 类概述

1.1 Object 类的地位

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#fdfdfe"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true }
}}%%

flowchart TB
    %% 样式定义
    classDef root fill:#e8f5e9,stroke:#4caf50,stroke-width:2.5px,color:#1b5e20;
    classDef mid fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef leaf fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
    classDef info fill:#fff3e0,stroke:#ff9800,stroke-dasharray: 5 5,color:#e65100;

    %% 核心继承结构
    subgraph Hierarchy ["Java 类继承体系"]
        direction TB
        Root["Object<br/>(所有类的根类)"]

        subgraph Level1 ["第一层继承"]
            StringNode["String"]
            NumberNode["Number"]
            CustomClass["你的自定义类"]
        end

        subgraph Level2 ["第二层继承 (以 Number 为例)"]
            IntegerNode["Integer"]
            DoubleNode["Double"]
            LongNode["Long"]
        end

        %% 建立连接
        Root ==> StringNode
        Root ==> NumberNode
        Root ==> CustomClass

        NumberNode --> IntegerNode
        NumberNode --> DoubleNode
        NumberNode --> LongNode
    end

    %% 特点说明
    subgraph Features ["Object 类核心特点"]
        F1["所有类直接或间接继承 Object"]
        F2["省略 extends 时编译器自动补全"]
        F3["所有对象均拥有 Object 的基础方法"]
    end

    %% 节点应用样式
    class Root root;
    class StringNode,NumberNode,CustomClass mid;
    class IntegerNode,DoubleNode,LongNode leaf;
    class F1,F2,F3 info;

    %% 布局辅助
    Hierarchy ~~~ Features
// 这两种写法等价
public class Person { }

public class Person extends Object { }

1.2 Object 类的 11 个方法

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart LR
    %% 核心根节点
    Root(["<b>Java Object 类方法全景图</b>"])

    %% 分支 1:比较与标识
    subgraph Group1 ["1. 对象比较与标识"]
        direction TB
        M1_1["equals(Object obj)<br/><small>对象相等性比较</small>"]
        M1_2["hashCode()<br/><small>返回对象哈希码</small>"]
        M1_3["toString()<br/><small>返回对象字符串表示</small>"]
    end

    %% 分支 2:对象克隆
    subgraph Group2 ["2. 对象克隆"]
        M2_1["clone()<br/><small>创建并返回对象副本</small>"]
    end

    %% 分支 3:类型信息
    subgraph Group3 ["3. 类型信息"]
        M3_1["getClass()<br/><small>获取运行时 Class 对象</small>"]
    end

    %% 分支 4:线程同步
    subgraph Group4 ["4. 线程同步 (Monitor)"]
        direction TB
        M4_1["wait() / wait(ms) / wait(ms, ns)<br/><small>线程进入等待状态</small>"]
        M4_2["notify()<br/><small>唤醒单个等待线程</small>"]
        M4_3["notifyAll()<br/><small>唤醒所有等待线程</small>"]
    end

    %% 分支 5:垃圾回收
    subgraph Group5 ["5. 垃圾回收"]
        M5_1["finalize()<br/><small>对象销毁前回调 (已废弃)</small>"]
    end

    %% 连接关系
    Root ==> Group1
    Root ==> Group2
    Root ==> Group3
    Root ==> Group4
    Root ==> Group5

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
    classDef deprecated fill:#ffebee,stroke:#f44336,stroke-width:1px,color:#b71c1c;

    %% 应用样式
    class Root main;
    class M1_1,M1_2,M1_3 main;
    class M2_1 storage;
    class M3_1 term;
    class M4_1,M4_2,M4_3 decision;
    class M5_1 deprecated;

1.3 方法详情表

方法 修饰符 返回类型 说明
getClass() public final native Class<?> 获取运行时类对象
hashCode() public native int 返回对象哈希码
equals(Object obj) public boolean 判断对象是否相等
clone() protected native Object 创建并返回对象副本
toString() public String 返回对象字符串表示
notify() public final native void 唤醒一个等待线程
notifyAll() public final native void 唤醒所有等待线程
wait() public final void 使当前线程等待
wait(long) public final native void 限时等待
wait(long, int) public final void 精确限时等待
finalize() protected void GC 回收前调用(已废弃)

native:表示方法由 C/C++ 实现,不是 Java 代码 final:表示方法不能被重写

1.4 核心方法源码

/**
 * Object 类核心方法源码(简化版)
 */
public class Object {

    // ===== 1. getClass() =====
    // 返回运行时类型,不能重写
    public final native Class<?> getClass();

    // ===== 2. hashCode() =====
    // 默认返回对象内存地址转换的整数
    public native int hashCode();

    // ===== 3. equals() =====
    // 默认比较引用地址
    public boolean equals(Object obj) {
        return (this == obj);
    }

    // ===== 4. clone() =====
    // 创建对象副本,需要实现 Cloneable 接口
    protected native Object clone() throws CloneNotSupportedException;

    // ===== 5. toString() =====
    // 默认返回:类名@十六进制哈希码
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    // ===== 6-10. 线程相关方法 =====
    public final native void notify();
    public final native void notifyAll();
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait(long timeout, int nanos) throws InterruptedException { ... }
    public final void wait() throws InterruptedException { ... }

    // ===== 11. finalize() =====
    // 已废弃,Java 9 标记 @Deprecated
    @Deprecated(since="9")
    protected void finalize() throws Throwable { }
}

二、== 运算符详解

2.1 == 的行为规则

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "fontFamily": "arial"
  },
  "flowchart": {
    "curve": "basis",
    "htmlLabels": true
  }
}}%%

flowchart TB
    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:2px,color:#0d47a1;
    classDef typeNode fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef logicNode fill:#f1f8e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef codeNode fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,color:#333,font-family:monospace;

    %% 核心结构
    Start(["== 运算符的行为"]) --> Root{"比较对象 a == b"}

    %% 分支1:基本类型
    Root --> Primitive["基本数据类型<br/>(Primitive Types)"]
    Primitive --> PrimLogic["比较【值】是否相等"]

    subgraph PrimExample ["基本类型示例"]
        direction TB
        PCode["int a = 5;<br/>int b = 5;"]
        PRes{"a == b → true"}
        PCode --- PRes
    end

    %% 分支2:引用类型
    Root --> Reference["引用数据类型<br/>(Reference Types)"]
    Reference --> RefLogic["比较【内存地址】<br/>是否指向同一对象"]

    subgraph RefExample ["引用类型示例"]
        direction TB
        RCode["Object o1 = new Object();<br/>Object o2 = new Object();"]
        RRes1["o1 == o2 → false"]
        RRes2["o1 == o1 → true"]
        RCode --- RRes1
        RCode --- RRes2
    end

    %% 连接示例
    PrimLogic ==> PrimExample
    RefLogic ==> RefExample

    %% 类应用
    class Start main;
    class Root typeNode;
    class Primitive,Reference typeNode;
    class PrimLogic,RefLogic logicNode;
    class PCode,RCode,PRes,RRes1,RRes2 codeNode;

2.2 基本类型的 == 比较

public class PrimitiveEqualsDemo {
    public static void main(String[] args) {
        // ===== 整数类型 =====
        int a = 100;
        int b = 100;
        System.out.println("int: " + (a == b));       // true

        long c = 100L;
        System.out.println("int == long: " + (a == c)); // true(自动类型提升)

        // ===== 浮点类型 =====
        double d1 = 0.1 + 0.2;
        double d2 = 0.3;
        System.out.println("double: " + (d1 == d2));  // false!(精度问题)
        System.out.println("d1 = " + d1);             // 0.30000000000000004

        // ===== 字符类型 =====
        char ch1 = 'A';
        char ch2 = 65;
        System.out.println("char: " + (ch1 == ch2));  // true('A'的ASCII码是65)

        // ===== 布尔类型 =====
        boolean bool1 = true;
        boolean bool2 = true;
        System.out.println("boolean: " + (bool1 == bool2)); // true
    }
}

2.3 引用类型的 == 比较

public class ReferenceEqualsDemo {
    public static void main(String[] args) {
        // ===== 普通对象 =====
        Person p1 = new Person("张三", 20);
        Person p2 = new Person("张三", 20);
        Person p3 = p1;

        System.out.println("p1 == p2: " + (p1 == p2));  // false(不同对象)
        System.out.println("p1 == p3: " + (p1 == p3));  // true(同一对象)

        // ===== String 特殊情况 =====
        String s1 = "Hello";
        String s2 = "Hello";
        String s3 = new String("Hello");

        System.out.println("s1 == s2: " + (s1 == s2));  // true(字符串常量池)
        System.out.println("s1 == s3: " + (s1 == s3));  // false(s3是新对象)

        // ===== 包装类特殊情况 =====
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println("i1 == i2: " + (i1 == i2));  // true(缓存池 -128~127)
        System.out.println("i3 == i4: " + (i3 == i4));  // false(超出缓存范围)
    }
}

2.4 == 比较内存图解

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    subgraph Primitive ["基本类型:比较栈中的值"]
        direction LR
        subgraph Stack1 ["栈内存"]
            A1["a = 100"]
            B1["b = 100"]
        end

        Compare1{"100 == 100"}
        Result1(["true"])

        A1 --> Compare1
        B1 --> Compare1
        Compare1 ==> Result1
    end

    subgraph Reference ["引用类型:比较栈中的地址"]
        direction TB

        subgraph MemoryLayout ["内存结构"]
            direction LR
            subgraph Stack2 ["栈内存 (存储地址)"]
                P1["p1 = 0x100"]
                P2["p2 = 0x200"]
                P3["p3 = 0x100"]
            end

            subgraph Heap ["堆内存 (实际对象)"]
                ObjA[("Person 对象 A<br/>(0x100)")]
                ObjB[("Person 对象 B<br/>(0x200)")]
            end

            P1 ==> ObjA
            P3 ==> ObjA
            P2 ==> ObjB
        end

        subgraph Logic ["逻辑判断"]
            direction LR
            Cond1{"p1 == p2<br/>(0x100 == 0x200)"} -.-> Res1(["false"])
            Cond2{"p1 == p3<br/>(0x100 == 0x100)"} ==> Res2(["true"])
        end
    end

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;

    class A1,B1,P1,P2,P3 main;
    class Compare1,Cond1,Cond2 decision;
    class Result1,Res1,Res2 term;
    class ObjA,ObjB storage;

三、equals() 方法详解

3.1 Object.equals() 默认实现

// Object 类中的默认实现
public boolean equals(Object obj) {
    return (this == obj);  // 默认就是比较引用!
}
%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef code fill:#f5f5f5,stroke:#90a4ae,stroke-width:1px,color:#263238;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef warning fill:#ffebee,stroke:#f44336,stroke-width:1px,color:#b71c1c;
    classDef solution fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;

    subgraph DefaultBehavior ["Object.equals() 默认行为解析"]
        direction TB

        Start(["开始调用 p1.equals(p2)"]) --> Logic{"内部实现逻辑"}

        Logic -- "默认实现" --> RawCode["return (this == obj);"]

        subgraph InstanceComparison ["堆内存对象对比"]
            direction LR
            P1[("p1 (张三, 20)")]
            P2[("p2 (张三, 20)")]
            P1 -. "物理地址不同" .-> P2
        end

        RawCode --> Compare{地址是否一致?}

        Compare -- "NO" --> ResultFalse(["结果: false"])

        Problem["<b>问题点</b><br/>内容完全相同的对象<br/>被判定为不相等"]

        ResultFalse ==> Problem
    end

    subgraph SolutionPath ["改进方案"]
        direction TB
        Overriding[["重写 equals() 方法"]]
        LogicChange["改为比较成员变量<br/>(name, age)"]
        ResultTrue(["预期结果: true"])

        Overriding --> LogicChange --> ResultTrue
    end

    Problem ==> Overriding

    %% 节点样式应用
    class Start,ResultFalse main;
    class RawCode,InstanceComparison code;
    class Logic,Compare decision;
    class Problem warning;
    class Overriding,ResultTrue solution;

3.2 String.equals() 源码分析

/**
 * String 类重写的 equals 方法(JDK 8)
 */
public boolean equals(Object anObject) {
    // 1. 首先检查是否是同一个对象(快速路径)
    if (this == anObject) {
        return true;
    }

    // 2. 检查类型是否是 String
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;

        // 3. 比较长度
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;

            // 4. 逐字符比较
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

流程图:

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TD
    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef error fill:#ffebee,stroke:#f44336,stroke-width:1.5px,color:#b71c1c;

    %% 节点定义
    Start(["开始比较 String.equals(anObject)"])
    CheckRef{"this == anObject<br/>(引用地址相同?)"}
    CheckType{"anObject instanceof String<br/>(是否为字符串?)"}
    CheckLength{"length 相同?"}
    CheckChars{"逐字符循环比较<br/>全部字符相同?"}

    RetTrue(["return true"])
    RetFalse(["return false"])

    %% 流程连接
    Start --> CheckRef

    CheckRef == "是 (YES)" ==> RetTrue
    CheckRef -- "否 (NO)" --> CheckType

    CheckType -. "否 (NO)" .-> RetFalse
    CheckType == "是 (YES)" ==> CheckLength

    CheckLength -. "否 (NO)" .-> RetFalse
    CheckLength == "是 (YES)" ==> CheckChars

    CheckChars == "是 (YES)" ==> RetTrue
    CheckChars -. "否 (NO)" .-> RetFalse

    %% 应用样式
    class Start,CheckLength,CheckChars main;
    class CheckRef,CheckType decision;
    class RetTrue term;
    class RetFalse error;

    %% 子图修饰
    subgraph Logic ["String.equals() 内部逻辑路径"]
        CheckRef
        CheckType
        CheckLength
        CheckChars
    end

3.3 正确重写 equals() 方法

public class Person {
    private String name;
    private int age;
    private Address address;

    // 构造方法、Getter/Setter 省略

    /**
     * 重写 equals 方法
     */
    @Override
    public boolean equals(Object obj) {
        // 1. 检查是否是同一个对象的引用
        if (this == obj) {
            return true;
        }

        // 2. 检查 obj 是否为 null
        if (obj == null) {
            return false;
        }

        // 3. 检查是否是同一个类型
        // 方式一:getClass()(严格类型匹配)
        if (getClass() != obj.getClass()) {
            return false;
        }

        // 方式二:instanceof(允许子类相等,但可能违反对称性)
        // if (!(obj instanceof Person)) {
        //     return false;
        // }

        // 4. 强制类型转换
        Person other = (Person) obj;

        // 5. 比较关键属性
        // 基本类型:用 ==
        if (this.age != other.age) {
            return false;
        }

        // 引用类型:用 equals(注意 null 处理)
        if (this.name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!this.name.equals(other.name)) {
            return false;
        }

        // 使用 Objects.equals() 更简洁
        // return age == other.age &&
        //        Objects.equals(name, other.name) &&
        //        Objects.equals(address, other.address);

        return true;
    }
}

3.4 使用 Objects.equals() 简化

import java.util.Objects;

public class Person {
    private String name;
    private int age;
    private Address address;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Person person = (Person) obj;

        // 使用 Objects.equals() 自动处理 null
        return age == person.age &&
               Objects.equals(name, person.name) &&
               Objects.equals(address, person.address);
    }
}

Objects.equals() 源码:

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

3.5 equals() 的五个规范

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB
    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef highlight fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef ruleBox fill:#ffffff,stroke:#cfd8dc,stroke-width:1px,color:#37474f;

    Title(["Java equals() 方法遵循的五大规范"])

    subgraph CoreRules ["核心等价关系规范"]
        direction TB

        Rule1["1. 自反性 (Reflexive)"]
        Desc1["x.equals(x) 恒为 true<br/>(对象必须等于自身)"]

        Rule2["2. 对称性 (Symmetric)"]
        Desc2["若 x.equals(y) 为 true<br/>则 y.equals(x) 必为 true"]

        Rule3["3. 传递性 (Transitive)"]
        Desc3["若 x.equals(y) 且 y.equals(z) 为 true<br/>则 x.equals(z) 必为 true"]

        Rule1 --- Desc1
        Rule2 --- Desc2
        Rule3 --- Desc3
    end

    subgraph StabilityRules ["稳定性与健壮性规范"]
        direction TB

        Rule4["4. 一致性 (Consistent)"]
        Desc4["只要对象状态未变<br/>多次调用结果必须一致"]

        Rule5["5. 非空性 (Non-nullity)"]
        Desc5["x.equals(null) 必须返回 false<br/>(任何对象不等于 null)"]

        Rule4 --- Desc4
        Rule5 --- Desc5
    end

    %% 连接标题与子图
    Title ==> CoreRules
    Title ==> StabilityRules

    %% 应用样式
    class Title highlight
    class Rule1,Rule2,Rule3,Rule4,Rule5 main
    class Desc1,Desc2,Desc3,Desc4,Desc5 ruleBox

四、== 与 equals() 对比

4.1 核心区别

对比项 == equals()
本质 运算符 方法
基本类型 比较值 不适用(基本类型没有方法)
引用类型 比较地址 默认比较地址,可重写比较内容
能否重写 不能 可以
null 处理 可以用于 null null.equals() 会 NPE

4.2 对比图解

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;

    %% 场景初始化
    Start(["<b>场景:比较两个 Person 对象</b><br/>p1 = new Person('张三', 20)<br/>p2 = new Person('张三', 20)"])

    subgraph DoubleEqual ["== 运算符比较"]
        direction TB
        Op1["p1 == p2"]
        Comp1["比较堆内存引用地址<br/>(0x100 == 0x200 ?)"]
        Res1(["false<br/>(不是同一个对象)"])

        Op1 --> Comp1
        Comp1 --> Res1
    end

    subgraph EqualsMethod ["equals() 方法比较"]
        direction TB
        Op2["p1.equals(p2)"]
        CheckOverride{"是否重写了<br/>equals 方法?"}

        NoOverride["未重写 (调用 Object 类)"]
        HasOverride["已重写 (业务逻辑)"]

        Comp2["比较引用地址<br/>(逻辑同 ==)"]
        Comp3["比较对象内容<br/>(name.equals && age ==)"]

        Res2(["false"])
        Res3(["true<br/>(内容逻辑相同)"])

        Op2 --> CheckOverride
        CheckOverride -- "No" --> NoOverride
        CheckOverride -- "Yes" --> HasOverride

        NoOverride --> Comp2
        HasOverride --> Comp3

        Comp2 --> Res2
        Comp3 ==> Res3
    end

    %% 顶层连接
    Start ~~~ DoubleEqual
    Start ~~~ EqualsMethod

    %% 应用样式
    class Start storage;
    class Op1,Op2 main;
    class CheckOverride decision;
    class Res1,Res2,Res3 term;

4.3 常见陷阱与最佳实践

public class EqualsTrapsDemo {
    public static void main(String[] args) {

        // ===== 陷阱 1:用 == 比较字符串内容 =====
        String input = new String("admin");

        // ❌ 错误写法
        if (input == "admin") {
            System.out.println("永远不会执行");
        }

        // ✅ 正确写法
        if (input.equals("admin")) {
            System.out.println("正确判断");
        }

        // ✅ 更好的写法:常量在前,避免 NPE
        if ("admin".equals(input)) {
            System.out.println("避免空指针");
        }

        // ===== 陷阱 2:用 == 比较包装类 =====
        Integer a = 128;
        Integer b = 128;

        // ❌ 可能出错(超出缓存范围)
        if (a == b) {
            System.out.println("可能不会执行");
        }

        // ✅ 正确写法
        if (a.equals(b)) {
            System.out.println("正确判断");
        }

        // ✅ 或者拆箱比较
        if (a.intValue() == b.intValue()) {
            System.out.println("正确判断");
        }

        // ===== 陷阱 3:null 调用 equals =====
        String str = null;

        // ❌ 会抛出 NullPointerException
        // if (str.equals("test")) { }

        // ✅ 使用 Objects.equals()
        if (Objects.equals(str, "test")) {
            System.out.println("安全比较");
        }
    }
}

五、hashCode() 方法详解

5.1 hashCode() 的作用

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true }
}}%%

flowchart TB

    %% 核心概念定义
    Start(["hashCode() 的作用"])
    Definition["返回对象的哈希码 (int) <br/> 主要用于哈希表快速定位"]

    subgraph Comparison ["查找效率对比"]
        direction LR

        subgraph Traditional ["没有 hashCode"]
            T1["逐个比较 equals()"]
            T2["时间复杂度: O(n)"]
            T1 --> T2
        end

        subgraph Optimized ["有了 hashCode"]
            O1["1. 计算 hashCode 定位桶位置"]
            O2["2. 桶内进行 equals() 比较"]
            O3["时间复杂度: 接近 O(1)"]
            O1 ==> O2 ==> O3
        end
    end

    %% 连接逻辑
    Start --> Definition
    Definition --> Comparison

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;

    %% 应用样式
    class Start,Definition main;
    class O1,O2,O3 term;
    class T1,T2 decision;

5.2 HashMap/HashSet 工作原理

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TD
    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;

    %% 流程节点
    Start(["开始: set.add(obj)"]) --> CalcHash["Step 1: 计算 hashCode<br/>int hash = obj.hashCode()"]
    CalcHash --> CalcIndex["Step 2: 计算桶位置<br/>int index = hash % 数组长度"]

    subgraph BucketProcess ["哈希表寻址与冲突检查"]
        CalcIndex --> CheckEmpty{"Step 3: 检查桶是否为空"}

        CheckEmpty -- "是 (null)" --> DirectInsert["直接插入到该索引位置"]
        CheckEmpty -- "否 (已有数据)" --> TraverseList["Step 4: 遍历链表/红黑树<br/>调用 equals() 逐一比较"]

        TraverseList --> EqualCheck{"是否存在相同元素?"}

        EqualCheck -- "找到相同<br/>(equals == true)" --> Discard["放弃添加 (保证唯一性)"]
        EqualCheck -- "未找到相同<br/>(equals == false)" --> ListInsert["添加到链表末尾或红黑树"]
    end

    %% 结束节点
    DirectInsert ==> Success(["添加成功"])
    ListInsert ==> Success
    Discard -.-> Fail(["添加失败 (已存在)"])

    %% 指派样式
    class Start,CalcHash,CalcIndex,TraverseList main;
    class CheckEmpty,EqualCheck decision;
    class Success,ListInsert,DirectInsert term;
    class Discard,Fail storage;

5.3 哈希表结构图

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    subgraph Container ["HashMap 内部数据结构 (Java 8+)"]
        direction TB

        subgraph ArrayLayer ["Node&lt;K,V&gt;[] table (数组/桶)"]
            direction LR
            B0["Index 0"]
            B1["Index 1"]
            B2["Index 2"]
            B3["Index 3"]
            B4["Index 4"]
            B5["Index 5"]
            B6["Index 6"]
            B7["Index 7"]
        end

        %% 链表结构 1
        B0 --> NodeA["Node A:1"]
        NodeA --> NodeB["Node B:2"]

        %% 空位
        B1 --> Null1(["null"])

        %% 链表结构 2
        B3 --> NodeC["Node C:3"]
        NodeC --> NodeD["Node D:4"]

        %% 链表转红黑树示意
        B5 --> NodeE["Node E:5"]
        NodeE --> NodeF["Node F:6"]
        NodeF --> NodeG["Node G:7"]
        NodeG -.-> Tree{{"红黑树节点<br/>TreeNode"}}

        subgraph Logic ["转换逻辑说明"]
            Rule["当链表长度 > 8 且数组长度 > 64 时"]
        end
    end

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef bucket fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
    classDef node fill:#ffffff,stroke:#546e7a,stroke-width:1px;
    classDef special fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef tip fill:#e8f5e9,stroke:#4caf50,stroke-dasharray: 5 5;

    class B0,B1,B2,B3,B4,B5,B6,B7 bucket;
    class NodeA,NodeB,NodeC,NodeD,NodeE,NodeF,NodeG node;
    class Tree special;
    class Rule tip;
    class Container main;

5.4 Object.hashCode() 默认实现

// Object 类中的默认实现(native 方法)
public native int hashCode();
%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB
    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef code fill:#f5f5f5,stroke:#90a4ae,stroke-width:1px,color:#263238,font-family:monospace;
    classDef warning fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef highlight fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;

    subgraph Header ["Object.hashCode() 默认行为解析"]
        direction TB
        Title["<b>默认实现:基于对象内部标识(Native)计算</b><br/>通常与内存地址相关,但由 JVM 具体实现决定"]
    end

    subgraph Characteristics ["核心特点"]
        direction LR
        C1["<b>一致性</b><br/>同一对象生命周期内<br/>hashCode 保持不变"]
        C2["<b>区分性</b><br/>不同对象通常拥有<br/>不同的 hashCode"]
        C3["<b>冲突性</b><br/>不同对象可能产生<br/>相同 hash (概率极低)"]
    end

    subgraph Example ["代码实例与内存表现"]
        direction TB
        CodeBlock["Person p1 = new Person('张三', 20);<br/>Person p2 = new Person('张三', 20);"]

        subgraph Memory ["JVM 堆内存状态"]
            direction LR
            Obj1["对象 p1<br/>(内容: 张三, 20)<br/><b>Hash: 123</b>"]
            Obj2["对象 p2<br/>(内容: 张三, 20)<br/><b>Hash: 456</b>"]
        end
    end

    subgraph Consequence ["默认行为的影响"]
        Impact{"内容相同<br/>但 Hash 不同"}
        Result["<b>HashSet / HashMap 失效</b><br/>p1 与 p2 会被视为两个独立元素<br/>存入集合,导致逻辑重复"]
    end

    %% 逻辑连接
    Title --> Characteristics
    Characteristics --> CodeBlock
    CodeBlock --> Obj1
    CodeBlock --> Obj2
    Obj1 -.-> Impact
    Obj2 -.-> Impact
    Impact ==> Result

    %% 类应用
    class Title,C1,C2,C3 main;
    class CodeBlock code;
    class Obj1,Obj2 highlight;
    class Impact,Result warning;

六、equals 和 hashCode 的关系

6.1 核心规范

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef must fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#1b5e20;
    classDef optional fill:#f5f5f5,stroke:#9e9e9e,stroke-dasharray: 5 5,color:#616161;

    subgraph Contract ["Java equals & hashCode 契约关系"]
        direction TB

        %% 规则 1 路径
        R1_Start{"a.equals(b) == true"}
        R1_Result(["必须相等 (Consistency)"])

        R1_Start == "强制要求" ==> R1_Result

        %% 规则 2 路径
        R2_Start{"a.hashCode() == b.hashCode()"}
        R2_True["a.equals(b) == true"]
        R2_False["a.equals(b) == false"]

        R2_Start -. "可能" .-> R2_True
        R2_Start -. "也可能 (哈希冲突)" .-> R2_False

        %% 规则 3 路径
        R3_Start{"a.equals(b) == false"}
        R3_Diff(["推荐:提高散列性能"])
        R3_Same(["允许:但产生冲突"])

        R3_Start ==> R3_Diff
        R3_Start -. "允许但性能下降" .-> R3_Same
    end

    %% 应用样式
    class R1_Start,R2_Start,R3_Start decision
    class R1_Result,R3_Diff must
    class R2_True main
    class R2_False,R3_Same optional

    %% 补充说明
    note1["<b>规则 1:</b><br/>如果两个对象相等,<br/>它们的 HashCode 必须相同"]
    note2["<b>规则 2:</b><br/>HashCode 相同不代表对象相等<br/>(Bucket 碰撞)"]

    note1 --- R1_Start
    note2 --- R2_Start

6.2 图解规则

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB
    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef success fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef error fill:#ffebee,stroke:#f44336,stroke-width:1.5px,color:#b71c1c;

    %% 核心逻辑
    Start(["开始判断对象关系"]) --> HashCheck{"hashCode 是否相等?"}

    %% hashCode 相等分支
    subgraph HashEqualGroup ["hashCode 相等 (存在潜在关联)"]
        HashCheck -- "相等" --> EqualsCheck{"equals 是否相等?"}

        EqualsCheck -- "相等" --> Case1["✅ 合法:对象完全相同"]
        EqualsCheck -- "不等" --> Case2["✅ 合法:哈希冲突 (Collision)"]
    end

    %% hashCode 不相等分支
    subgraph HashNotEqualGroup ["hashCode 不相等"]
        HashCheck -- "不相等" --> Case3["✅ 合法:equals 必定不等"]
    end

    %% 违规约束
    Constraint{"违反原则的情况"} -.-> Illegal["❌ 非法:equals 相等但 hashCode 不等"]

    %% 节点样式应用
    class Start main;
    class HashCheck,EqualsCheck decision;
    class Case1,Case2,Case3 success;
    class Illegal error;

    %% 补充说明
    note1["注:Java 规范要求若 equals 相等,则 hashCode 必须相等"]
    Case1 ~~~ note1
    style note1 fill:#fafafa,stroke:#ccc,stroke-dasharray: 5 5,color:#666;

6.3 为什么重写 equals 必须重写 hashCode?

/**
 * 只重写 equals,不重写 hashCode 的问题演示
 */
public class BrokenPerson {
    private String name;
    private int age;

    public BrokenPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 只重写了 equals
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        BrokenPerson other = (BrokenPerson) obj;
        return age == other.age && Objects.equals(name, other.name);
    }

    // ❌ 没有重写 hashCode!
}

public class BrokenDemo {
    public static void main(String[] args) {
        BrokenPerson p1 = new BrokenPerson("张三", 20);
        BrokenPerson p2 = new BrokenPerson("张三", 20);

        // equals 返回 true
        System.out.println("p1.equals(p2): " + p1.equals(p2));  // true

        // 但 hashCode 不同!
        System.out.println("p1.hashCode(): " + p1.hashCode());  // 例如:366712642
        System.out.println("p2.hashCode(): " + p2.hashCode());  // 例如:1829164700

        // ===== 在 HashSet 中出问题 =====
        Set<BrokenPerson> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println("set.size(): " + set.size());  // 2 ❌ 应该是 1

        // ===== 查找失败 =====
        BrokenPerson p3 = new BrokenPerson("张三", 20);
        System.out.println("set.contains(p3): " + set.contains(p3));  // false ❌
    }
}

问题分析:

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    subgraph Analysis ["问题根源分析:未重写 hashCode 导致重复"]
        direction TB

        subgraph Objects ["对象状态 (p1.equals(p2) == true)"]
            P1["<b>对象 p1</b><br/>hashCode: 366712642"]
            P2["<b>对象 p2</b><br/>hashCode: 1829164700"]
        end

        subgraph Process ["HashSet.add() 内部逻辑"]
            direction LR
            Calc1["计算 p1 桶位<br/>366712642 % 16 = <b>2</b>"]
            Calc2["计算 p2 桶位<br/>1829164700 % 16 = <b>12</b>"]
        end

        subgraph Storage ["HashMap 桶数组 (Buckets)"]
            direction LR
            B2[("桶 [2]")]
            B12[("桶 [12]")]
            Other["其他桶..."]
        end

        subgraph Result ["最终结果"]
            Fail["<b>逻辑冲突</b><br/>p1 与 p2 位于不同桶<br/>无法触发 equals() 检查"]
        end
    end

    %% 连线关系
    P1 ==> Calc1
    P2 ==> Calc2

    Calc1 ==> B2
    Calc2 ==> B12

    B2 -.-> Result
    B12 -.-> Result

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
    classDef alert fill:#ffebee,stroke:#f44336,stroke-width:2px,color:#b71c1c;

    class P1,P2,Calc1,Calc2 main;
    class B2,B12,Other storage;
    class Result,Fail alert;

6.4 正确实现

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // ✅ 正确重写 equals
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && Objects.equals(name, person.name);
    }

    // ✅ 正确重写 hashCode(使用相同的属性)
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

证:

public class CorrectDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 20);
        Person p2 = new Person("张三", 20);

        System.out.println("p1.equals(p2): " + p1.equals(p2));      // true
        System.out.println("p1.hashCode(): " + p1.hashCode());      // 相同
        System.out.println("p2.hashCode(): " + p2.hashCode());      // 相同

        Set<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println("set.size(): " + set.size());            // 1 ✅
        System.out.println("set.contains(new Person(\"张三\", 20)): "
                + set.contains(new Person("张三", 20)));            // true ✅
    }
}

七、Objects.hash() 源码分析

7.1 源码

/**
 * java.util.Objects 类
 */
public final class Objects {

    public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }
}

/**
 * java.util.Arrays 类
 */
public class Arrays {

    public static int hashCode(Object[] a) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }
}

7.2 为什么使用 31 作为乘数?

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB
    %% 核心公式展示
    Formula(["<b>hash = 31 * hash + element.hashCode()</b>"])

    %% 三大核心理由
    subgraph Reasons ["为什么选择 31?"]
        direction TB

        subgraph Math ["1. 数学特性 (奇素数)"]
            direction TB
            Prime["<b>素数 (Prime)</b><br/>减少哈希冲突的可能性"]
            Odd["<b>奇数 (Odd)</b><br/>避免乘法溢出时低位丢失信息"]
        end

        subgraph Perf ["2. 性能优化 (JVM)"]
            direction TB
            Shift["<b>位运算优化</b><br/>31 * i == (i << 5) - i"]
            Fast["<b>执行效率高</b><br/>移位和减法比传统乘法更快"]
        end

        subgraph Practice ["3. 工业实践 (经验值)"]
            direction TB
            Uniform["<b>分布均匀</b><br/>在大数据测试中冲突率极低"]
            Standard["<b>标准实现</b><br/>JDK String 等类广泛采用"]
        end
    end

    %% 连接关系
    Formula ==> Reasons
    Math ~~~ Perf
    Perf ~~~ Practice

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef highlight fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef subbox fill:#f9f9f9,stroke:#cfd8dc,stroke-dasharray: 5 5;

    class Formula highlight;
    class Prime,Odd,Shift,Fast,Uniform,Standard main;
    class Math,Perf,Practice subbox;

7.3 手动实现 hashCode

public class Person {
    private String name;
    private int age;
    private Address address;

    // 方式一:使用 Objects.hash()(推荐)
    @Override
    public int hashCode() {
        return Objects.hash(name, age, address);
    }

    // 方式二:手动实现
    @Override
    public int hashCode() {
        int result = 17;  // 非零初始值
        result = 31 * result + (name == null ? 0 : name.hashCode());
        result = 31 * result + age;
        result = 31 * result + (address == null ? 0 : address.hashCode());
        return result;
    }

    // 方式三:Java 7+ 使用 Objects 工具类
    @Override
    public int hashCode() {
        return Objects.hash(name, age, address);
    }
}

八、最佳实践

8.1 使用 IDE 自动生成

/**
 * IDEA 自动生成的 equals 和 hashCode
 * 快捷键:Alt + Insert → equals() and hashCode()
 */
public class Person {
    private String name;
    private int age;
    private Address address;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
               Objects.equals(name, person.name) &&
               Objects.equals(address, person.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, address);
    }
}

8.2 使用 Lombok 注解

import lombok.EqualsAndHashCode;
import lombok.Data;

// 方式一:只生成 equals 和 hashCode
@EqualsAndHashCode
public class Person {
    private String name;
    private int age;
}

// 方式二:使用 @Data(包含 equals、hashCode、toString、getter、setter)
@Data
public class Person {
    private String name;
    private int age;
}

// 方式三:排除某些字段
@EqualsAndHashCode(exclude = {"id", "createTime"})
public class Person {
    private Long id;
    private String name;
    private int age;
    private LocalDateTime createTime;
}

// 方式四:只包含某些字段
@EqualsAndHashCode(of = {"name", "age"})
public class Person {
    private Long id;
    private String name;
    private int age;
}

8.3 注意事项

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    subgraph CorePrinciple ["核心原则:一致性与不变性"]
        P1["1. 属性同步:equals 与 hashCode 必须使用完全相同的属性集合"]
        P2["2. 不变性:参与计算的属性应尽量为不可变 (final)"]
    end

    subgraph Implementation ["实现细节与最佳实践"]
        direction LR
        subgraph Safety ["安全性保障"]
            S1{"类型检查"}
            S1 -- "推荐" --> S1_A["使用 getClass() <br/>(保证对称性)"]
            S1 -. "慎用" .-> S1_B["使用 instanceof <br/>(继承场景易出错)"]

            S2["Null 安全"]
            S2 --> S2_A["使用 Objects.equals()"]
            S2 --> S2_B["使用 Objects.hash()"]
        end
    end

    subgraph RiskWarning ["风险警示:违规后果"]
        R1[("HashSet/HashMap")]
        R2["修改已存入对象的属性"]
        R3["hashCode 发生改变"]
        R4["无法检索到该对象 (内存泄漏风险)"]

        R1 --> R2 --> R3 --> R4
    end

    %% 逻辑连接
    CorePrinciple ==> Implementation
    P2 -. "如果不遵守" .-> RiskWarning

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef warning fill:#ffebee,stroke:#f44336,stroke-width:1.5px,color:#b71c1c;

    class P1,P2,S2_A,S2_B main;
    class S1 decision;
    class S1_A,S1_B term;
    class R1,R2,R3,R4 warning;

九、toString() 方法

9.1 默认实现

// Object 类中的默认实现
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Person p = new Person("张三", 20);
System.out.println(p.toString());
// 输出:com.example.Person@1b6d3586

9.2 重写 toString()

public class Person {
    private String name;
    private int age;

    // 方式一:手动拼接
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }

    // 方式二:使用 String.format
    @Override
    public String toString() {
        return String.format("Person{name='%s', age=%d}", name, age);
    }

    // 方式三:使用 StringBuilder
    @Override
    public String toString() {
        return new StringBuilder()
                .append("Person{")
                .append("name='").append(name).append("'")
                .append(", age=").append(age)
                .append("}")
                .toString();
    }

    // 方式四:使用 StringJoiner(Java 8+)
    @Override
    public String toString() {
        return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]")
                .add("name='" + name + "'")
                .add("age=" + age)
                .toString();
    }
}

十、核心总结

10.1 速查表

方法 默认行为 是否需要重写 重写目的
equals() 比较引用地址 ✅ 通常需要 比较对象内容
hashCode() 基于内存地址 ✅ 通常需要 配合哈希表使用
toString() 类名@哈希码 ✅ 建议重写 输出有意义的信息
clone() 浅拷贝 ⚠️ 按需重写 实现深拷贝
getClass() 返回 Class 对象 ❌ 不能重写 final 方法
wait/notify 线程等待/唤醒 ❌ 不能重写 final 方法
finalize() 空实现 ❌ 已废弃 不建议使用

10.2 记忆口诀

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    %% 核心标题
    Title(["Java 对象比较与哈希口诀指南"])

    subgraph Comparison ["== 与 equals:核心区别"]
        C1["基本比值,引用比址"]
        C2["equals 默认同 ==<br/>重写才比内容"]
        C1 --- C2
    end

    subgraph HashContract ["equals 与 hashCode:契约关系"]
        H1["equals 相等 ==> hashCode 必等"]
        H2["hashCode 相等 -.-> equals 不一定"]
        H3["重写一个,必须重写另一个"]
        H4["使用相同的属性来计算"]

        H1 --- H2
        H2 --- H3
        H3 --- H4
    end

    subgraph BestPractice ["实践口诀:避坑与提效"]
        P1["比较字符串/包装类用 equals"]
        P2["'常量'.equals(变量)<br/>常量放前面,空指针不来"]
        P3["IDE 一键生成<br/>Lombok @Data 更简洁"]

        P1 --- P2
        P2 --- P3
    end

    %% 逻辑连接
    Title ==> Comparison
    Comparison ==> HashContract
    HashContract ==> BestPractice

    %% 样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;

    class Title main;
    class C1,C2 decision;
    class H1,H2,H3,H4 term;
    class P1,P2,P3 main;

10.3 核心要点图

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#e3f2fd",
    "primaryTextColor": "#0d47a1",
    "primaryBorderColor": "#2196f3",
    "lineColor": "#546e7a",
    "fontSize": "14px",
    "tertiaryColor": "#f5f5f5"
  },
  "flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%

flowchart TB

    %% 核心样式定义
    classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
    classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
    classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
    classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;

    Root(["Java Object 核心机制总结"])

    subgraph Hierarchy ["1. 根类地位"]
        RootNode["Object 类"]
        Methods["拥有 11 个核心方法"]
        Focus["核心重写:equals / hashCode / toString"]

        RootNode --> Methods
        Methods --> Focus
    end

    subgraph Comparison ["2. == 与 equals 区别"]
        Compare{"比较方式"}
        OpEqual["== 运算符"]
        MethodEqual["equals() 方法"]

        Compare --> OpEqual
        Compare --> MethodEqual

        OpEqual -- "基本类型" --> V1["比较具体数值"]
        OpEqual -- "引用类型" --> V2["比较内存地址"]

        MethodEqual -- "默认" --> V2
        MethodEqual -- "重写后" --> V3["比较对象内容"]
    end

    subgraph HashMechanism ["3. hashCode 与哈希表"]
        HashRole["快速定位 (HashMap/HashSet)"]
        Logic{"查找逻辑"}
        Step1["1. 比较 hashCode"]
        Step2["2. 调用 equals()"]

        HashRole --> Logic
        Logic --> Step1
        Step1 -- "哈希冲突" --> Step2
    end

    subgraph Rules ["4. 重写规范 & 约束"]
        Must["重写 equals 必须重写 hashCode"]
        Property["两者必须基于相同属性计算"]

        subgraph Contract ["五大特性"]
            C1["自反性 / 对称性"]
            C2["传递性 / 一致性"]
            C3["非空性 (Not-null)"]
        end

        Must --> Property
        Property --> Contract
    end

    subgraph BestPractice ["5. 最佳实践"]
        ObjectsUtil["使用 Objects.equals() / hash()"]
        AutoTool["IDE 自动生成 / Lombok (@Data)"]
        SafeCheck["'常量'.equals(变量) 规避 NPE"]
    end

    %% 逻辑连接
    Root ==> Hierarchy
    Hierarchy ==> Comparison
    Comparison ==> HashMechanism
    HashMechanism ==> Rules
    Rules ==> BestPractice

    %% 应用样式
    class RootNode,Root main;
    class Compare,Logic decision;
    class Root term;
    class ObjectsUtil,AutoTool,SafeCheck storage;