本文基于 OracleJDK 11, HotSpot 虚拟机。

Object 定义

Object 类是类层次结构的根。每个类都有 Object 类作为超类。所有对象,包括数组等,都实现了这个类的方法。

静态代码块

在Object类的最开始部分,有如下四行代码:

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

native 方法主要用于通过调用 C 或 C++ 实现的本地方法来对底层操作系统的访问。

扩展

native 关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如 C 和C++)实现的文件中。

Java 语言本身不能对操作系统底层进行访问和操作,但是可以通过 JNI(Java Native Interface)接口调用其他语言来实现对底层的访问。

**JNI **全称是 Java Native Interface,即 Java 本机接口,是 Java 和 Native 间的通信桥梁。Java 调用 Native,可以去调用非 Java 实现的库,扩充 Java 的使用场景;反之 Native 调用 Java,可以在别的语言里面调用 Java。

类的 static 静态代码块会在类初始化时调用,其目的是为该类中包含的除了registerNatives()方法以外的所有本地方法(被 native 关键字修饰的方法)进行注册。

构造函数

 @HotSpotIntrinsicCandidate
public Object() {}
  • 无参构造函数主要是创建一个新的 Object 对象。
  • @HotSpotIntrinsicCandidate 注解特定于 HotSpot 虚拟机,它表明带注解的方法可能被 HotSpot 内在化,在虚拟机内存在更高效的实现来替换被注解修饰的方法以提高性能。该注解是 Java 库内部的,与应用程序没有任何关联。

方法

getClass()

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

该方法是 native 方法,作用是返回当前对象运行时的类。返回的 Class 对象是被static synchronized方法锁定的对象。

实际的结果类型是 Class<? extends |X|>,X 是对调用 getClass 的表达式的静态类型擦除。如下所示,代码中不需要强制转换:

Number n = 0;
Class<? extends Number> c = n.getClass();

Class 对象表示运行时此对象的类。

hashCode()

public native int hashCode();

该方法是 native 方法,作用是返回当前对象的哈希码值。支持此方法是为了便于对 java.util.HashMap 等提供的哈希表。

hashCode 的通用规则是:

  • 每当在 Java 应用程序执行期间对同一对象多次调用它时,该方法始终返回相同的整数。如果 equals() 中使用的信息没有被修改。从应用程序的一次执行到相同的应用程序的一次执行,此整数不必保持一致。
  • 如果两个对象根据调用 equals() 方法相等,则在每个对象上调用 hashCode() 方法必须产生相同的整数结果。
  • 如果两个对象根据调用 equals()方法不相等,在每个对象上调用hasCode()方法不要求必须产生不同的整数结果(因为可能存在哈希碰撞)。

equals()

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

该方法指示其他对象是否“等于”当前对象,判断两个对象是否具有相同的引用(对象的内存地址)。

equals() 函数必须满足以下五点条件:

  • 反身性:对于任何 x, x.equals(x) 应该返回 true。
  • 对称性:对于任何 x 和 y,x.equals(y) 应该返回 true 当且仅当 y.equals(x) 返回 true 。
  • 传递性:对于任何 x,y, 还有 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,那么 x.equals(z) 应该返回 true。
  • 一致性:对于任何 x 和 y,在对象没有被改变的情况下,多次调用 x.equals(y) 应该总是返回 true 或者 false。
  • 对于任何非 null 的 x,x.equals(null) 应该返回 false。

类 Object 的equals()方法在对象上实现了最有区别的等价关系,也就是说,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用的是同一对象的时候,x==y 返回 true。

默认情况下从超类 Object 继承而来的 equals() 方法与 == 是完全等价的,比较的都是对象的内存地址,但我们可以重写 equals()方法,使其按照我们的需求的方式进行比较。

注意

每当重写 equals()方法时,通常都需要重写hashCode()方法,以便维护hashCode()方法的常规约定,该方法申明相等的对象必须具有相同的 hashCode 值。如果子类不重写,将会默认使用父类对象的 hashCode() 方法,导致对象内容一致但对象内存地址不一致。

因为equals()方法比较消耗性能且效率低,一般有大量数据需要快速的对比的话,会先比对hashCodehashCode相等的再使用equals进行比较。

Java 中常见的一些默认类都会重写 hashCode()equals() 方法,如 String 类等。

clone()

 @HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;

该方法是 native 方法,作用是创建并返回此对象的副本。

注意:clone 方法本身没有实现 Cloneable 接口,但在调用 clone 方法时需要实现 Cloneable 接口并重写,否则会抛出 CloneNotSupportedException 异常以表示无法克隆。

通常重写克隆需要满足以下条件:

  • x.clone() != x 为 true (对象引用指向堆内存地址不同)
  • x.clone().getClass() == x.getClass() 为 true(相同的运行时类)
  • x.clone().equals(x) 为 true (对象属性内容相同,由于 Object 的 equals() 默认为 this == obj,所以需要重写)

但这不是绝对的,因为浅克隆和深克隆的区别。

扩展

浅克隆

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换而言之,浅拷贝仅仅复制所拷贝的对象,而不复制它所引用的对象.

深克隆

被复制对象的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

toString()

返回对象的字符串表示形式。

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

此方法默认返回 类名@无符号十六进制表示形式组成对象的哈希码。该方法是为了更简洁清晰且信息丰富的表示对象内容,易于阅读,所以建议所有子类重写该方法。

notify()、notifyAll()

@HotSpotIntrinsicCandidate
public final native void notify(); @HotSpotIntrinsicCandidate
public final native void notifyAll();

这两个方法都是 native 方法,作用都是唤醒正在此对象的监视器上等待的线程,区别在于 notify() 是唤醒单线程,而 notifyAll()是唤醒所有线程。

如果有任何线程正在等待该对象,则选择其中一个被唤醒。该选择是任意的,并由实施自行决定。线程通过调用 wait 方法之一在对象的监视器上等待。

在当前线程放弃对该对象的锁定之前,被唤醒的线程将无法继续。被唤醒的线程将以通常的方式与可能正在积极竞争以在此对象上同步的任何其他线程竞争

该方法只能由作为该对象监视器所有者的线程调用,否则会抛出 IllegalMonitorStateException 异常。

扩展

每个对象都有一个”锁“,即监视器 Monitor,而且每个对象都有一个同步队列(EntrySet)和等待队列(WaitSet),同步队列和等待队列里面都存放着线程对象的引用。

notify() 是对 notifyAll() 的一个优化,但它有很精确的应用场景,并且要求正确使用。不然可能导致死锁。正确的场景应该是 WaitSet 中等待的是相同的条件,唤醒任一个都能正确处理接下来的事项,如果唤醒的线程无法正确处理,务必确保继续 notify() 下一个线程,并且自身需要重新回到 WaitSet 中。

wait()、wait(long timeout)、wait(long timeout, int nanos)

 public final void wait() throws InterruptedException {
wait(0L);
}
public final native void wait(long timeoutMillis) throws InterruptedException; 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++;
} wait(timeoutMillis);
}

wait(long timeout) 方法是 native 方法,作用是让当前线程释放 CPU 占用资源并且释放对象的”锁“,直到该对象执行了 notify()/noyifyAll()方法,或者过了 timeoutMillis 的等待时间(单位:毫秒),或者被其他线程调用了该线程的 interrupt()方法打断,该线程会被唤醒。

wait()方法就是调用了 wait(0L),代表无限等待,只能通过该对象执行了 notify()/noyifyAll()方法、被打断,该线程会被唤醒。

wait(long timeoutMillis, int nanos) 方法只是添加了一个范围在0~999999纳秒的附加时间int nanos,本质上还是调用了 wait(long timeout)

如果当前线程不是对象监视器的所有者,则会抛出 IllegalMonitorStateException 异常。

如果当前线程在等待之前或期间被任何线程中断,则会抛出 InterruptedException 异常。

线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,可以将 wait 配合 while 循环使用,苏醒后进行条件检查,如果不满足则 继续 wait() 直至条件满足再往下执行。

finalize()

@Deprecated(since="9")
protected void finalize() throws Throwable { }

当垃圾收集确定不再有对该对象的引用时,由对象上的垃圾收集器调用。子类覆盖finalize()方法来处理系统资源或执行其他清理。

每个对象的 finalize() 方法只能被系统执行一次,该方法类似析构函数但不等价。

死亡逃逸,可以在对象被回收之前在 finalize 方法里面重新与其他对象(其他对象不能是即将被回收的对象)建立关联即可,但机会只有一次。

在JDK9及其之后已被废弃使用。原因是最终确定机制本质上是有问题的。最终确定会导致性能问题、死锁和挂起。终结器中的错误可能导致资源泄漏;如果不再需要,则无法取消最终确定;并且在对不同对象的 finalize 方法的调用之间没有指定顺序。此外,无法保证最终确定的时间。finalize() 方法可能仅在无限期延迟之后才在可终结对象上调用

对象持有非堆资源的类应该提供一种方法来启用这些资源的显式释放,它们可以实现 java.lang.AutoCloseable

java.lang.ref.Cleanerjava.lang.ref.PhantomReference 提供了更灵活、更有效的方法来在对象变得无法访问时释放资源。

Java源码分析 | Object的更多相关文章

  1. Java源码分析 | CharSequence

    本文基于 OracleJDK 11, HotSpot 虚拟机. CharSequence 定义 CharSequence 是 java.lang 包下的一个接口,是 char 值的可读序列, 即其本身 ...

  2. Java源码分析:关于 HashMap 1.8 的重大更新(转载)

    http://blog.csdn.net/carson_ho/article/details/79373134 前言 HashMap 在 Java 和 Android 开发中非常常见 而HashMap ...

  3. Java源码分析之LinkedList

    LinkedList与ArrayList正好相对,同样是List的实现类,都有增删改查等方法,但是实现方法跟后者有很大的区别. 先归纳一下LinkedList包含的API 1.构造函数: ①Linke ...

  4. Java源码之Object

    本文出自:http://blog.csdn.net/dt235201314/article/details/78318399 一丶概述 JAVA中所有的类都继承自Object类,就从Object作为源 ...

  5. Java源码分析:Guava之不可变集合ImmutableMap的源码分析

    一.案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样-- public static final Map<String,String& ...

  6. JAVA源码分析-HashMap源码分析(二)

    本文继续分析HashMap的源码.本文的重点是resize()方法和HashMap中其他的一些方法,希望各位提出宝贵的意见. 话不多说,咱们上源码. final Node<K,V>[] r ...

  7. JAVA源码分析-HashMap源码分析(一)

    一直以来,HashMap就是Java面试过程中的常客,不管是刚毕业的,还是工作了好多年的同学,在Java面试过程中,经常会被问到HashMap相关的一些问题,而且每次面试都被问到一些自己平时没有注意的 ...

  8. 【转】【java源码分析】Map中的hash算法分析

    全网把Map中的hash()分析的最透彻的文章,别无二家. 2018年05月09日 09:08:08 阅读数:957 你知道HashMap中hash方法的具体实现吗?你知道HashTable.Conc ...

  9. 【Java源码分析】LinkedList类

    LinkedList<E> 源码解读 继承AbstractSequentialList<E> 实现List<E>, Deque<E>, Cloneabl ...

随机推荐

  1. React设置proxy后依旧报CROS错误

    1.判断表单数据是否为后端接收的类型 POST GET2.axios自动转换问题 手动添加标头这份表单数据包括了files (二进制数据)而标头显示是JSON格式 不符 所以报CROS 更多文章请移步 ...

  2. 2021.05.04【NOIP提高B组】模拟 总结

    T1 题目大意, \(S_{i,j}=\sum_{k=i}^j a_k\) ,求 \(ans=\min\{ S_{i,j}\mod P|S_{i,j}\mod P\ge K \}\) 其中 \(i\l ...

  3. Apache Shiro反序列化漏洞(Shiro550)

    1.漏洞原理: Shiro 是 Java 的一个安全框架,执行身份验证.授权.密码.会话管理 shiro默认使用了CookieRememberMeManager,其处理cookie的流程是:得到rem ...

  4. 【Redis】quicklist

    Redis List 在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现.当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现 ...

  5. 【Srping】事务的执行原理(一)

    在使用事务的时候需要添加@EnableTransactionManagement注解来开启事务,那么就从@EnableTransactionManagement入手查看一下事务的执行原理. @Enab ...

  6. React中render Props模式

    React组件复用 React组件复用的方式有两种: 1.render Props模式 2.高阶组件HOC 上面说的这两种方式并不是新的APi. 而是利用Raect自身的编码特点,演化而来的固定编码写 ...

  7. JSON.toJSONString(joinPoint.getArgs())报错getOutputStream() has already been called for this response

    nested exception is java.lang.IllegalStateException: It is illegal to call this method if the curren ...

  8. Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程

    上文,我们看了IOC设计要点和设计结构:紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的. ...

  9. React技巧之表单提交获取input值

    正文从这开始~ 总览 在React中,通过表单提交获得input的值: 在state变量中存储输入控件的值. 在form表单上设置onSubmit属性. 在handleSubmit函数中访问输入控件的 ...

  10. PHP生成图形验证码

    在建站过程中,很多时候都会需要用户验证验证码等操作,比如:注册.登录.发表评论.获取资源等等,一方面可以验证当前用户的行为是否是爬虫.机器人等情况,给网站数据统计产生影响:另一方面可以防止用户大量刷取 ...