Java基础3-1:Object类、equals和hashCode的关系

一、前言

  本文内容摘自《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》一书,2022年出版,作者 张洪亮(@Hollis),阿里巴巴技术专家,著有《Java工程师成神之路》系列文章,《Java工程师成神之路》电子书已开源,可在阿里云开发者社区免费下载。书籍内容比电子书内容要丰富,内容有修改,有需要的读者可以购买正版书籍。
 
  【如何成神:先搬砖,再砌砖,后造砖!】
 
  本文由 @大白有点菜 原创,请勿盗用,转载请说明出处!如果觉得文章还不错,请点点赞,加关注,谢谢!

  《Java工程师成神之路》下载地址为:
  https://developer.aliyun.com/ebook/395?spm=a2c6h.20345107.ebook-index.24.4c927863j10ats

深入理解Java核心技术:写给Java工程师的干货笔记(基础篇) 

二、第3章 Java对象【Object类、equals和hashCode的关系】

1、Object类

  Java中所有类都默认继承一个类——java.lang.Object类,是所有类的超类。
 
Object类的方法:

  • clone():创建并返回此对象的副本。
  • equals(Object obj):指示其它对象是否“等于”此对象。
  • hashCode():返回对象的哈希码值。
  • notify():唤醒正在等待这个对象的监视器的单个线程。
  • notifyAll():唤醒正在等待这个对象的监视器的所有线程。
  • wait():使当前线程进入等待状态,直到另外一个线程调用此对象的 notify() 方法或 notifyAll() 方法。
  • finalize():当垃圾回收器决定回收某对象时,就会运行该对象的 finalize() 方法。

 
Object类代码:

package java.lang;

public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    public final native Class<?> getClass();

    public native int hashCode();

    public boolean equals(Object obj) {
        return (this == obj);
    }

    protected native Object clone() throws CloneNotSupportedException;

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    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 {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

    public final void wait() throws InterruptedException {
        wait(0);
    }
    
    protected void finalize() throws Throwable { }
}

 

2、equals和hashCode的关系

  Java 中每个类都隐式地继承自java.lang.Object类,并且会继承equals()和hashcode()两个方法。
 
  在没有重写这两个方法的情况下,equals()的默认实现与“==”操作符一致,即如果引用变量指向同一个对象,则返回true。例如:

public boolean equals(Object obj) {
    return (this == obj);
}

 
【equals()举例(书中例子)】
 
Person类代码(样例1):

/**
 * Person类1
 * @author 大白有点菜
 */
public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
}

 
主程序代码(样例1):

/**
 * Equals样例1
 * @author 大白有点菜
 */
public class EqualsApp1 {
    public static void main(String[] args) {
        Person person1 = new Person("君莫笑");
        Person person2 = new Person("君莫笑");
        Person person3 = person1;
        System.out.println("person1 == person2 ? " + (person1.equals(person2)));
        System.out.println("person1 == person3 ? " + (person1.equals(person3)));
    }
}

 
运行结果(样例1):

person1 == person2 ? false
person1 == person3 ? true

 
  如果想判断两个对象的内容是否相等,则需要重写equals()和hashcode()方法。例如:
 
Person类代码(样例2):

import java.util.Objects;

/**
 * Person类2
 * @author 大白有点菜
 */
public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        Person person = (Person) obj;
        return Objects.equals(name, person.name);
    }
}

 
主程序代码(样例2):

/**
 * Equals样例2
 * @author 大白有点菜
 */
public class EqualsApp2 {
    public static void main(String[] args) {
        Person person1 = new Person("文西与阿漆");
        Person person2 = new Person("文西与阿漆");
        Person person3 = person1;
        System.out.println("person1 == person2 ? " + (person1.equals(person2)));
        System.out.println("person1 == person3 ? " + (person1.equals(person3)));
    }
}

 
运行结果(样例2):

person1 == person2 ? true
person1 == person3 ? true

 

3、equals的多种写法

(1)第一种:IntellijIDEA默认的实现方式。

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }

    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }

    Person person = (Person) obj;
    return name != null ? name.equals(person.name) : person.name == null;
}

(2)第二种:基于Apache Commons Lang框架的实现方式。

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }

    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }

    Person person = (Person) obj;
    return new EqualsBuilder().append(name, person.name).isEquals();
}

(3)第三种:基于Guava框架的实现方式。

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }

    if (obj == null || getClass() != obj.getClass()) {
        return false;
    }

    Person person = (Person) obj;
    return Objects.equals(name, person.name);
}

4、equals和hashCode

  hashCode()方法作用是返回对象的哈希值,返回值类型是int。这个哈希值的作用是确定该对象在哈希表中位置。

  Jsohua Bloch在Effective Java中对hashCode有这样的描述:你必须在每个重写equals()的类中重写hashCode()。如果不这么做,那么将违反Object.hashCode()的一般约定,这将阻止类与所有基于散列的集合(包括HashMap、HashSet和Hashtable)一起正常工作。

  在HashMap和HashSet等基于散列的集合中,会使用对象的hashCode值来确定该对象应该如何存储在集合中,并且再次使用hashCode来在其集合中定位对象。
 
  如果有一个类,只重写了equals方法,而没有重写hashCode方法,那么会发生什么问题?例如:
 
Person类代码(样例3):

import java.util.Objects;

/**
 * Person类3
 * @author 大白有点菜
 */
public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        Person person = (Person) obj;
        return Objects.equals(name, person.name);
    }
}

 
主程序代码(样例3):

/**
 * Equals样例3
 * @author 大白有点菜
 */
public class EqualsApp3 {
    public static void main(String[] args) {
        Person person1 = new Person("文西与阿漆");
        Person person2 = new Person("文西与阿漆");
        HashSet<Person> set = new HashSet<>();

        set.add(person1);
        set.add(person2);

        System.out.println("set.size() = " + set.size());
    }
}

 
运行结果:

set.size() = 2

 
  以上代码定义了一个 Person 类,只重写了equals方法,并没有重写hashCode方法,然后定义两个值内容一样的对象,尝试它们放入一个不能重复的Set,最后输出这个Set的元素个数是2,说明Set认为这两个对象不是相等的。
 
  两个对象相等的严格定义是:对象内容相等(equals()的结果),并且哈希值也要相等(hashCode的结果)。
 
  给Person添加一个hashCode方法,重新执行,看看结果如何。例如:
 
Person类代码(样例4):

import java.util.Objects;

/**
 * Person类4
 * @author 大白有点菜
 */
public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        Person person = (Person) obj;
        return Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

 
主程序代码(样例4):

/**
 * Equals样例4
 * @author 大白有点菜
 */
public class EqualsApp4 {
    public static void main(String[] args) {
        Person person1 = new Person("文西与阿漆");
        Person person2 = new Person("文西与阿漆");
        HashSet<Person> set = new HashSet<>();

        set.add(person1);
        set.add(person2);

        System.out.println("set.size() = " + set.size());
    }
}

 
运行结果(样例4):

set.size() = 1

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大白有点菜

你的鼓励决定文章的质量

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值