Object 类的常见方法有哪些?
Object 类是一个特殊的类,是所有类的父类。它主要提供了以下 11 个方法:
/**
* 尚硅谷2024新版3小时速通Docker教程
* https://www.sanzhishu.top/2696.html
*/// native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写
public final native Class<?> getClass()// native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的 HashMap
public native int hashCode()// 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等
public boolean equals(Object obj)// native 方法,用于创建并返回当前对象的一份拷贝
protected native Object clone() throws CloneNotSupportedException// 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法
public String toString()// native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个
public final native void notify()// native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程
public final native void notifyAll()// native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间
public final native void wait(long timeout) throws InterruptedException// 多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒
public final void wait(long timeout, int nanos) throws InterruptedException// 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
public final void wait() throws InterruptedException// 实例被垃圾回收器回收的时候触发的操作
protected void finalize() throws Throwable { }
== 和 equals() 的区别
-
== 对于基本类型和引用类型的作用效果是不同的:
- 对于基本数据类型来说,== 比较的是值。
- 对于引用数据类型来说,== 比较的是对象的内存地址。 Java 只有值传递,所以对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
-
equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。equals() 方法存在两种使用情况:
- 类没有重写 equals()方法:通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。
- 类重写了 equals()方法:一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true,即认为这两个对象相等。String 中的 equals 方法就是被重写过的。
hashCode() 是什么?返回值是内存地址吗?
-
hashCode方法用于返回对象的哈希码(散列码)值,hashCode值是一个整数值,这个方法可以提高哈希表(如Hashtable提供的哈希表)的性能,确定该对象在哈希表中的索引位置。
-
hashCode() 定义在 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。hashCode在Object类中有native修饰,是本地方法,该方法的方法体不是Java实现的,是由C/C++实现的,最后编译为.dll文件,然后由Java调用。
-
hashCode 可以是内存地址,也可以不是内存地址,在 Oracle OpenJDK8 中有六种生成方式来生成 hashCode,默认使用的是一种随机数生成的方法,这是由于垃圾回收时对象需要发生移动,因此使用内存地址会带来不便。
为什么要有 hashCode()?
Java的集合有两类,一类是 List,还有一类是 Set。前者有序可重复,后者无序不重复。当我们在Set中插入的时候怎么判断是否已经存在该元素呢,可以通过equals方法。但是如果元素太多,用这样的方法就会比较慢。
于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域。
hashCode 方法返回的就是一个哈希码值,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用 equals 方法的次数就大大降低了,相应就大大提高了执行速度。
hashCode() 和 equals() 都是用于比较两个对象是否相等。
-
那为什么 JDK 还要同时提供这两个方法呢?
这是因为在一些容器(比如 HashMap、HashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高。我们在前面也提到了添加元素进HashSet的过程,如果 HashSet 在对比的时候,同样的 hashCode 有多个对象,它会继续使用 equals() 来判断是否真的相同。也就是说 hashCode 帮助我们大大缩小了查找成本。
-
那为什么不只提供 hashCode() 方法呢?
这是因为两个对象的hashCode 值相等并不代表两个对象就相等。
-
那为什么两个对象有相同的 hashCode 值,它们也不一定是相等的?
因为 hashCode() 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关(所谓哈希碰撞也就是指的是不同的对象得到相同的 hashCode)。
为什么重写 equals() 时必须重写 hashCode() 方法?
之前在 HashSet 检查元素重复的过程中,判断的过程是:
- 如果两个对象的hashCode 值相等,那这两个对象不一定相等(哈希碰撞)。
- 如果两个对象的hashCode 值相等并且equals()方法也返回 true,我们才认为这两个对象相等。
- 如果两个对象的hashCode 值不相等,我们就可以直接认为这两个对象不相等。
如果重写 equals() 时没有重写 hashCode() 方法的话,就可能会导致 hashCode 值却不相等被判断为不同的对象,但是事实上两个对象是重复的(equals 为 true)。