JDK 15 java.util.Object类源码阅读记录

一、分析Object类源码的原因

Object类是类层次结构的根,Java所有类的始祖,是整个类继承结构的顶端,也是最抽象的一个类。所有类都实现Object类的方法,因此熟悉这个类的所有服务十分重要。

二、新知识

2.1@HotSpotIntrinsicCandidate注解

如果被@HotSpotIntrinsicCandidate标注代表,代表该方法在HotSpot VM中有一套高效的实现,该高效基于CPU指令,HotSpot VM维护的高效实现会替代JDK源码的实现来获得更高效率。Java 9引入的新特性。

2.2native修饰符

定义Native Method时不提供实现体,实现体由Java调用非Java代码的接口,Native Method的实现体由非java语言实现。

2.3 synchronization claims(同步声明)

三、 Object所有方法分析

3.1 构造方法

public class Object {
		@HotSpotIntrinsicCandidate
//创建新的对象
    public Object() {}

如果被@HotSpotIntrinsicCandidate标注代表,代表该方法在HotSpot VM中有一套高效的实现,该高效基于CPU指令,HotSpot VM维护的高效实现会替代JDK源码的实现来获得更高效率。Java 9引入的新特性。

3.2 getClass()

		@HotSpotIntrinsicCandidate
    public final native Class<?> getClass();

返回此Object的运行时类,返回的对象是由表示类的**static synchronized方法锁定**的。实际返回的结果是Class<? extends |X|>类型,其中? extends |X|是对调用
{@code getClass}的表达式的静态类型的擦除。例如,此代码碎片中不需要强制转换:
Number n = 0; Class<? extends Number> c = n.getClass();
返回值:表示此对象的运行时类的类对象。
类加载的第一阶段类的加载是将 .class文件加载到内存,并生成一个 java.lang.Class对象的过程。 getClass()方法就是获取这个对象,这是当前类的对象在运行时类的所有信息的集合。这个方法是反射三种方式之一。

3.3 hashCode()

 		@HotSpotIntrinsicCandidate
    public native int hashCode();

返回对象的哈希代码值,此方法是为了给像java.util.HashMap提供的哈希表提高效率。
hashCode的常规协定:

  1. Java应用程序执行过程中,每当同一对象多次调用hashCode(),只要没有修改对象equals()比较中使用的信息,
    hashCode()必须返回相同整数。
  2. 从应用程序的一次执行到此相同应用程序的另一次执行,hashCode()返回的整数不必一致
  3. 如果根据equles()方法判断两对象相等,这两个对象的equals()必须返回相同整数。
  4. 如果根据equles()方法判断两对象不相等,这两个对象不是必须返回不同整数可以返回相同整数
  5. 程序员们应该注意,hashCode()不相等的对象产生不同整数结果可以提高哈希表性能
    返回值:
    此对象的哈希代码值。
    实现要求:
    只要合理可行,类对象定义的hashCode方法会为不同的对象返回不同的整数。
    请参阅:
    equals(Object), System.identityHashCode

计算 hashCode都使用了 31作为基础乘数,为什么使用 31呢?

  1. 因为31不大不小,且是个质数。可以减少哈希冲突。如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。而如果选择一个100以上的质数,得出的哈希值会超出int的最大范围。
  2. result*31=(result<<5)-result(左边 31*2=62,右边 2*2^5-2=62)。31是**2的5次方-1,也就是11111。**运算时可以通过位移运算提升性能,JVM虚拟机支持这种优化。

3.4 equals()

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

指出其他对象与此对象是否相等。
equals()在非空对象引用上实现了相同关系:

  1. 自反性:对任何非空引用x,x.equals(x)返回true
  2. 对称性:对任何非空引用x和y,当且仅当y.equals(x)返回truex.equals(y)才返回true.
  3. 可传递性:对任何非空引用x、y和z,如果x.equals(y)返回truey.equals(z)返回true
    那么x.equals(z)应该返回true
  4. 一致性:对于任何非空的引用值x和y,在没有修改对象上equals比较中使用的信息前提下,
    x.equals(y)的多次调用始终返回true或始终返回false
  5. 对任何非空引用值x,x.equals(null)应返回false
    Object类的equals()实现了对象的相等的最有识别力的相等关系
    也就是说,对任何非空引用x和y,当且仅当x和y引用同一对象(x==y值为true)时,此方法才返回true。
    请注意,必需在重写equals()时重写hashCode(),即必需重写hashCode()后才能重写equals()方法!
    以便维护hashCode()的常规约定:相等的对象必须具有相等哈希值。
    参数:
    obj–要与之进行比较的参考对象。
    返回值:
    如果此对象与obj参数相同,则为true;否则返回false。
    hashCode(), java.util.HashMap

为什么要重写equals()?

因为如果不重写equals方法,当将自定义对象放到 map或者 set中时;如果这时两个对象的 hashCode相同,就会调用 equals方法进行比较,这个时候会调用 Object中默认的 equals方法,而默认的 equals方法只是比较了两个对象的引用是否指向了同一个对象,这样就会将重复对象存入 map或者 set中。这就破坏了 mapset不能存储重复对象的特性,会造成内存溢出

3.5 clone()

		@HotSpotIntrinsicCandidate
    protected native Object clone() throws CloneNotSupportedException;

这是一个 protected方法,提供给子类重写。但需要实现 Cloneable接口,这是一个标记接口,如果没有实现,当调用 object.clone()方法,会抛出 CloneNotSupportedException
创建并返回此对象的拷贝。copy的精确含义也许取决于对象的类。对一个对象x的一般意图表达:x.clone() != xtrue,而x.clone().getClass() == x.getClass()为true,但以上两种情况不是绝对的。按惯例返回的对象应该通过调用super.clone获得,
如果一个类和其所有超类(除了Object)遵守此惯例,则x.clone().getClass() == x.getClass()
按照约定,通过clone()返回的对象应该独立于调用的对象(正在被clone).要实现这种独立性,要修改super.clone返回的对象的一个或多个字段。典型地,这意味着,拷贝任何 正在被克隆的且具有内部深入结构的对象 的组成成分的可变对象(复制构成被克隆对象内部"深层结构"的任何可变对象),并用拷贝副本的引用代替这些对象的引用。
如果一个类仅包含基本字段或不可变对象的引用,这种情况通常super.clone返回的对象不需要修改任何字段。
Objectclone()执行特定的clone操作:
首先,如果此对象的类没实现Cloneable接口,抛出CloneNotSupportedException
注意所有数组被认为实现了Cloneable接口,并且数组T[]clone方法返回类型为T[]T可以是任何引用或基本类型。
否则此方法创建这个对象类的新实例,并用此相符合对象的相应字段初始化新实例的所有字段,就像通过赋值。
因此clone()执行对象的“浅 shallow copy”而不是"deep copy"操作。
class对象本身不实现Cloneable接口,因此对Object类对象调用clone方法抛出运行时异常。
也就是必须对实现接口Cloneable的对象调用clone。
返回值:
此实例的克隆。
抛出:
CloneNotSupportedException–如果对象的类不支持可克隆接口。重写clone方法的子类也可以引发此异常,以指示无法克隆实例。
请参阅:
Cloneable

clone的对象是一个新的对象;但原对象与 clone对象的 字段却是同一个引用,这表明, super.clone方法对成员变量如果是引用类型,进行是浅拷贝。

那如果我们要进行深拷贝怎么办呢?

:如果成员变量是引用类型,想实现深拷贝,则成员变量也要实现 Cloneable接口,重写 clone方法。

3.6 toString()

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

返回对象的字符串表示形式,通常,toString()返回"文本表示"此对象字符串。结果该是简练但信息丰富的表达便于一个人阅读,建议所有子类重写此方法。Object类的toString()返回一个此类作为对象的实例的名称、at字符’@'和对象哈希代码的无符号十六进制表示形式组成。句话说,此方法返回一个等于以下值的字符串:getClass().getName()+'@'+Integer.toHexString(hashCode())
返回值:
对象的字符串表示形式。

我们思考一下为什么需要toString方法?

可以这么理解:返回当前对象的字符串表示,可以将其打印方便查看对象的信息,方便记录日志信息提供调试。我们可以选择需要表示的重要信息重写到 toString方法中。

3.7 notify()/notifyAll()

如果当前线程获得了当前对象锁,调用 wait(),将锁释放并阻塞;这时另一个线程获取到了此对象锁,并调用此对象的 notify()/notifyAll()方法将之前的线程唤醒。这些方法都是 publicfinal的,不可被重写。

/*唤醒一个正在该对象监视器上等待的单个线程。如果任何多个线程正在等待该对象,
它们其中一个被选择唤醒。选择是任意的,实现有自行决定的自由。
只要一个线程中一个对象调用了wait()方法,线程就在这个对象监视器上等待。
在当前线程放弃对该对象的锁定之前,唤醒的线程都无法继续。*/
		@HotSpotIntrinsicCandidate
    public final native void notify();
/*唤醒此对象监视器上所有在等待的线程,一个线程通过调用众多wait()中的一个在对象监视器上等待。
唤醒的线程在当前线程放弃对该对象的锁定之前都无法继续。唤醒的线程将以正常方式与其他任何线程竞争,
这些线程可能在积极竞争在此对象上同步:例如,唤醒的线程在成为下一个锁定此对象的线程时没有可靠的特权或缺点。
notifyAll()只能由作为此对象监视器所有者的线程调用。线程通过以下三种方式之一成为对象监视器的所有者:
通过执行该对象的同步实例方法。
通过执行同步该对象的synchronized语句体。
对于类类型的对象,执行该类的同步静态方法。
一次只能有一个线程拥有对象的监视器。
抛出:
IllegalMonitorStateException–如果当前线程不是此对象监视器的所有者。
请参阅:
notifyAll(), wait()*/
		@HotSpotIntrinsicCandidate
    public final native void notifyAll();

注意:调用 notify()后,阻塞线程被唤醒,可以参与锁的竞争,但可能调用 notify()方法的线程还要继续做其他事,锁并未释放,所以我们看到的结果是,无论 notify()是在方法一开始调用,还是最后调用,阻塞线程都要等待当前线程结束才能开始。

为什么 wait()/notify()方法要放到 Object中呢?
因为每个对象都可以成为锁监视器对象,所以放到 Object中,可以直接使用。

3.8 wait()/ wait(long)/ wait(long,int)

这三个方法是用来线程间通信用的,作用是阻塞当前线程,等待其他线程调用 notify()/notifyAll()方法将其唤醒。这些方法都是 public final的,不可被重写。

/*使当前线程等待,直到它被唤醒,典型地例子是通过被通知(notify)或被中断(interrupt)。在所有方面,
wait()表现的像是调用了wait(0L, 0),有关详细信息,请参阅wait(long,int)方法的规范。
抛出:
IllegalMonitorStateException–如果当前线程不是对象监视器的所有者
InterruptedException–如果任何线程在当前线程等待之前或期间中断了当前线程。
引发此异常时,当前线程的中断状态将被清除。*/
		public final void wait() throws InterruptedException {
        wait(0L);
    }

/*使当前线程等待,直到它被唤醒,典型地例子是通过被通知(notify)或被中断(interrupt),
或直到经过一定的真实(事实)时间。在所有方面,wait(long timeoutMillis)
表现的像是调用了wait(timeoutMillis,0),有关详细信息,请参阅wait(long,int)方法的规范。
参数:
timeoutMillis–等待的最长时间,以毫秒为单位
抛出:
IllegalArgumentException–如果timeoutMillis为负值
IllegalMonitorStateException–如果当前线程不是对象监视器的所有者
InterruptedException–如果任何线程在当前线程等待之前或期间中断了当前线程。引发此异常时,当前线程的中断状态将被清除。
请参阅:
notify(), notifyAll(), wait(), wait(long, int)*/
		public final native void wait(long timeoutMillis) throws InterruptedException;

/*使当前线程等待,直到它被唤醒,典型地例子是通过被通知(notify)或被中断(interrupt),
或直到经过一定的真实(事实)时间。当前线程必须拥有此对象的监视器锁。
有关线程成为监视器锁所有者的方式的描述,请参见notify()。
此方法导致当前线程(此处称为T)将自身置于此对象的wait set中,然后放弃此对象上的任何和所有**同步声明**。
请注意,只有此对象上的锁被放弃;当线程等待时,此线程上其他对象会保持线程调用wait()时的锁状态。
接下来出于线程调度目的,线程T变得无效(禁用?)、并处于**休眠状态**、直到以下情况之一发生:
1. 一些其他线程为此对象调用notify(),而线程T恰好被任意选择为要唤醒的线程。
2.  一些其他线程为此对象调用notifyAll()。
3. 一些其他线程中断线程T。
4. 明确规定的真实时间已消逝,或多或少。真实时间的大小由表达式1000000 * timeoutMillis + nanos
如果timeoutMillis和nanos都为0,则不考虑实时性,线程将等待其他原因之一唤醒。
5. 线程T被谬误的唤醒。(见下文)
线程T被因为此对象从wait set中删除并因为线程调度而重新启用。
它以通常的方式与其他线程竞争在对象上同步的权利,一旦线程T恢复了对对象的控制,线程T对对象的
所有同步声明都将恢复到原来调用wait()时的状态。然后线程T从wait()的调用返回。
因此,从wait()返回时,对象和线程T的同步状态与调用wait()时完全相同。
线程可以在不被通知、中断或超时的情况下唤醒,这就是所谓的虚假唤醒。虽然这种情况在实践中很少
发生,但应用程序必须通过本应导致线程被唤醒的条件测试来防范这种情况,并在条件不满足时继续等待。
请参见以下示例。有关此主题的更多信息,请参阅Brian Goetz和其他人的Java并发实践(Java Concurrency in Practice)
(Addison Wesley,2006)中的第14.2节“条件队列”,或Joshua Bloch的Effective Java第二版
(Addison Wesley,2008)中的第69项。
如果当前线程在等待之前或等待期间被任何线程中断,则抛出InterruptedException。引发此异常时,
当前线程的中断状态将被清除。在此对象的锁定状态之前如上述恢复之前,不会引发此异常。
参数:
timeoutMillis–等待的最长时间,以毫秒为单位
nanos–附加时间,以纳秒为单位,范围为0-999999(含0-999999)
抛出:
IllegalArgumentException–如果timeoutMillis为负值,或者如果Nano的值超出范围
IllegalMonitorStateException–如果当前线程不是对象监视器的所有者
InterruptedException–如果任何线程在当前线程等待之前或期间中断了当前线程。引发此异常时,
当前线程的中断状态将被清除。
API 说明:
建议的到达等待的方式,是在while循环中等待时检查调用等待的条件,如下例所示。
除其他外,这种方法避免了由虚假唤醒引起的问题。
synchronized (obj) {
   while (<condition does not hold> and <timeout not exceeded>) {
       long timeoutMillis = ... ; // recompute timeout values
       int nanos = ... ;
       obj.wait(timeoutMillis, nanos);
   }
   ... // Perform action appropriate to condition or timeout
}
*/
		public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
        if (timeoutMillis < 0) {
            throw new IllegalArgumentException("timeoutMillis value is negative");
        }

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

        if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
            timeoutMillis++;
        }

        wait(timeoutMillis);
    }

注意:

  1. 此方法只能在当前线程获取到对象的锁监视器之后才能调用,否则会抛出 IllegalMonitorStateException异常。
  2. 调用 wait方法,线程会将锁监视器进行释放;而 Thread.sleep,Thread.yield()并不会释放锁。
  3. wait方法会一直阻塞,直到其他线程调用当前对象的 notify()/notifyAll()方法将其唤醒;而 wait(long)是等待给定超时时间内(单位毫秒),如果还没有调用 notify()/nofiyAll()会自动唤醒; wait(long,int)如果第二个参数大于 0并且小于 999999,则第一个参数 +1作为超时时间;

3.9 finalize()

此方法是在垃圾回收之前,JVM会调用此方法来清理资源。此方法可能会将对象重新置为可达状态,导致JVM无法进行垃圾回收。

我们知道java相对于C++很大的优势是程序员不用手动管理内存,内存由jvm管理;如果我们的引用对象在堆中没有引用指向他们时,当内存不足时,JVM会自动将这些对象进行回收释放内存,这就是我们常说的垃圾回收。但垃圾回收没有讲述的这么简单。

finalize()方法具有如下4个特点:

  1. 永远不要主动调用某个对象的 finalize()方法,该方法由垃圾回收机制自己调用;
  2. finalize()何时被调用,是否被调用具有不确定性;
  3. JVM执行可恢复对象的 finalize()可能会将此对象重新变为可达状态;
  4. JVM执行 finalize()方法时出现异常,垃圾回收机制不会报告异常,程序继续执行。
/*当garbage collection查明不再有该对象的引用时,由垃圾回收器调用该对象的finalize()。
子类重写finalize()以处置系统资源或执行其他清理。
finalize的一般规约是,在当Java virtual machine确定不再有任何方法可供任何尚未终止的线程访问
此对象时,调用该对象的finalize(),除了其他准备终结的对象或类的finalize()造成的结果。
finalize()可以执行任何操作,包括使此对象再次可供其他线程使用;然而,finalize的通常用途是在
对象被不可撤销地丢弃之前执行清理操作。例如,一个表示输入/输出连接的对象的finalize()可能会执行
显式I/O事务,以在该对象被**永久丢弃之前断开连接**。
Object类的finalize()不执行特殊操作,简单的正常返回。Object的子类可以覆盖此定义。
Java编程语言不保证哪个线程将调用任何给定对象的finalize()。然而可以保证,调用finalize的线程
在调用finalize时不会持有任何用户可见的同步锁。如果finalize()抛出了未捕获的异常,则该异常将
被忽略,并且该对象的终结将终止。
在为对象调用finalize()之后,当Java虚拟机再次确定不再有任何方法可供任何尚未终止的线程访问
此对象之前,不会采取任何进一步的操作,包括对其他准备定案(finalized)对象或类的任何操作。
然后指出的对象可能被丢弃。
Java虚拟机不会为任何给定对象(就是任何对象)调用一次以上finalize()(就是不会调用第二次)。
finalize()引发的任何异常都会导致暂停此对象的定案(finalization),但在其他方面却被忽视了。
过时的:
定稿机制本身就有问题。定案可能导致性能问题、死锁和挂起。终结器中的错误可能导致资源泄漏;
如果不再需要定案,则无法取消定案;并且在调用不同对象的finalize()的调用之间不指定顺序。
此外,没有关于最后定案时间的保证。finalize()可能仅在无限延迟(如果有的话)之后才对
可终结对象调用。类们的实例如果持有非堆资源应该提供一个明确(显示)的对那些资源的释放,
类们的实例也该**实现**自动关闭如果允许的话。
ref.Cleaner和ref.PhantomReference提供了更灵活、更高效的方法,在对象无法访问时释放资源。
抛出:
Throwable–此方法引发的异常
API说明:
嵌入非堆资源的类有许多清理这些资源的选项。该类必须确保每个实例的生存期比它嵌入的任何资源的
生存期都长。当嵌入进对象的资源在使用时,保证对象保持可访问。子类应该避免重写finalize(),
除非子类嵌入了必须在实例被收集之前清理的非堆资源。不像构造器,终结器调用不会自动成链。
如果子类重写finalize,它必须显式调用超类finalizer。为了防止异常过早终止finalize链,子类应该
使用try-finally块来确保super.finalize()总被调用,例如
@Override
protected void finalize() throws Throwable {
   try {
       ... // cleanup subclass state
   } finally {
       super.finalize();
   }
}*/
		@Deprecated(since="9")
    protected void finalize() throws Throwable { }

四、阅读笔记

vt.是及物动词,可直接加宾语,不能单独使用

4.1 美词

erasure n.擦除    invoked v. 引援,调用    execution n.处决    consistent adj.一致的
comparison n.比较    modify vt.修改 performance n.性能    reflexive adj. 反身的    
symmetric adj.对称的    if and only if当且仅当    transitive adj.及物的、可传递的    
invocations n.调用    discriminating adj.有识别力的    maintain v.保持    
precise adj.准确的    obtain v.获得    convention n.习俗    mutable adj.可变的    
comprise vt.组成、包括    internal n.内脏、本质 adj.内部的    correspond vi.相一致
assignment n. 赋值    shallow adj.肤浅的    textually 文本上    informative adj.提供有用信息
concise adj.简练的    subclasses n.子类    hexadecimal adj.十六进制的    arbitrary adj.任意的
discretion n.自行决定的自由    proceed vi.继续 n.收益    relinquish v.放弃、出让
privilege n.特权    elapsed v.流逝    dormant adj.休眠的、蛰伏的    invoke v.调用
arbitrarily adv.任意的   nano n.豪威    nanosecond n.毫秒    spurious adj.虚假的、谬误的
reenable 重新启用,使再能    quo ante 维持现状    concurrence n.同一、一致 
concurrency n.并发性    while loop while循环    determine v.查明、决定
dispose vt.布置、清除    access n.道路 vt.访问、到达、存储、调用    finalize vt.定案
explicit adj.明确的    guarantee v.保证    terminate v.adj.终结    mechanism n.机制
inherent adj.固有的    performance issues 性能问题    indefinite adj.无限期的
embed v.嵌入    premature adj.提前的    terminate v.adj.终止    cast v.n.强制、铸造

4.2 美句:

  1. It is<i>consistent</i>: for any non-null reference values{@code x} and {@code y}, multiple invocations of{@code x.equals(y)} consistently return {@code true} or consistently return {@code false}, **provided no information used in {@code equals} comparisons on the objects is modified.provided conj.在…条件下**
  2. Typically, this means copying any mutable objects that comprise the internal "deep structure" of the object being cloned and replacing the references to these objects with references to the copies.copying修饰的mutable object,comprise翻译成组成,means后面有个and。
  3. Note that all arrays are considered to implement the interface Cloneable and that the return type of the clone method of an array type T[] is T[] where T is any reference or primitive type.好多of。
  4. The class Object does not itself implement the interface Cloneable, so calling the clone method on an object whose class is Object will result in throwing an exception at run time.本身原来这样写。
  5. This method should only be called by a thread that is the owner of this object's monitor.调用原来是called
  6. This method causes the current thread (referred to here as T) to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object. Note that only the locks on this object are relinquished;**any other objects on which the current thread may be synchronized remain locked while the thread waits.断句好难**
    举个例子:线程如果因为m(m>1)个资源被阻塞,那么得到1个资源时,只是这个资源可以从wait状态恢复,,其他的(m-1)个资源依旧是wait状态。.因为当前线程的运行还需要其他(m-1)个资源,因此此时的线程还是不能够运行的.
  7. but is otherwise ignored.这是什么

4.3 短语:

By convention按照惯例、initialize ... with ...用…初始化…、referred to here as T这里称为、thread schedule线程调度、guard against提防、防范、dispose of丢掉、清除、revocable可撤销的、irrevocably不可撤销的、discard抛弃、打出(无用的牌)、guard against提防、防范

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值