引言

我们都知道,在Java中,Object是所有类的超类,所有的类其实都是隐含继承自Object类的,所以extends Object默认是不用写的,当然你写了也不会错。所有的类都可以使用Object类中的方法,下面我们按源码的顺序分别来介绍。

Object类中的常用方法有:toString(),getClass(),hashCode(),equals(),clone(),finalize(),其中,定义为final类型,不能重写的方法:getClass(),notify(),notifyAll(),wait()。

Object类中有部分方法是由native关键字修饰的,这代表使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。所以native关键字的函数都是操作系统实现的。

1.registerNatives方法

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

native修饰的方法,通常有c或c++实现。这个方法表示的是在类被加载时,调用 registerNatives()方法进行一些跟系统有关的方法调用,而这个方法的实现就在java.dll中(里面会根据不同系统来执行不同的底层操作)。

下面是JDK1.6中关联的C语言代码(来自OpenJDK 6):

static JNINativeMethod methods[] = {{"hashCode",    "()I",                    (void *)&JVM_IHashCode},{"wait",        "(J)V",                   (void *)&JVM_MonitorWait},{"notify",      "()V",                    (void *)&JVM_MonitorNotify},{"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},{"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{(*env)->RegisterNatives(env, cls,methods, sizeof(methods)/sizeof(methods[0]));
}

2.getClass方法

public final native Class<?> getClass();

getClass返回运行时的类类型,final修饰不能被子类继承,native表示也不是由Java实现的。一般与getName方法一起使用。它能获取类的定义信息,然后通过反射的方式获取类的元信息和方法信息,包括函数和字段。

3.hashCode方法

public native int hashCode();

hashCode被native修饰为本地方法,该方法返回该对象的哈希码值,重写了equals方法一般都要重写hashCode方法。注意下面几点:

  1. hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
  2. 如果两个对象相同,就是满足于equals(Java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
  3. 如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面第2点;
  4. 两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

4.equals方法

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

equals直接判断this和obj的值是否相等,即判断两个对象是否为同一个对象,通过==判断,表示只有当两个对象的内存地址值是相同的他们才是同一个对象。如果你希望两个不同地址但内容相同的对象在进行equals方法比较时,就需要对equals方法进行重写。比如String的equals就重写了,分为两个逻辑,先比较==,如果相等直接返回true;如果不等接下来再把字符串分成字符数组,一个一个字符进行比较,如果字符都相等,也返回true,代码如下:

    //被String重写的equals方法public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}

5.clone方法

protected native Object clone() throws CloneNotSupportedException;

clone被native修饰为本地方法,protected保护方法,实现对象的浅拷贝,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

在某些场景中,我们需要获取到一个对象的拷贝用于某些处理。这时候就可以用到Java中的Object.clone方法进行对象复制,得到一个一模一样的新对象。但是在实际使用过程中会发现:当对象中含有可变的引用类型属性时,在复制得到的新对象对该引用类型属性内容进行修改,原始对象响应的属性内容也会发生变化,这就是"浅拷贝"的现象。表明浅拷贝在拷贝引用类型属性时,只拷贝了该属性的引用。如果希望拷贝后的对象是完全的新对象,那就需要重写clone方法或者采用序列化的方式进行对象的复制。

6.toString方法

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

toString方法返回一个字符串,类名+@+哈希值的16进制无符号整数形式。该方法用得比较多,一般子类都有覆盖。

7.notify方法

public final native void notify();

notify被native修饰为本地方法,不可被重写,该方法唤醒在该对象上等待的某个线程。

8.notifyAll方法

public final native void notifyAll();

notifyAll被native修饰为本地方法,不可被重写,该方法唤醒在该对象上等待的所有线程。

9.wait(long timeout)方法

public final native void wait(long timeout) throws InterruptedException;

wait被native修饰为本地方法,不可被重写,调用该方法后当前线程进入睡眠状态,让当前线程等待,使线程等待指定长的时间(毫秒)。在其他线程调用了notify或是notifyAll方法或是达到指定的时长,该线程就可以被调度。其他线程调用了interrupt中断该线程,就抛出一个InterruptedException异常。

10.wait(long timeout, int nanos)方法

    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 >= 500000 || (nanos != 0 && timeout == 0)) {timeout++;}wait(timeout);}

wait不可被重写,和wait(long timeout)方法本质相同,允许更好地控制在放弃之前等待通知的时间,实时量,以毫微秒计算,计算公式如下:1000000*timeout+nanos,特别是wait(0, 0) 表示和wait(0)相同,当前线程必须拥有该对象的监视器。

11.wait方法

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

实际上调用的是wait(long timeout)方法只不过timeout传的是0,只有调用了notify或是notifyAll方法,线程才会被唤醒。

12.finalize方法

protected void finalize() throws Throwable {}

该方法是protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法,它是一个空实现方法。该方法与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性。调用这个方法只是建议gc去调用这个方法,但是还是不能保证一定会被调用。 
finalize的执行周期:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。

参考资料:

  1. https://blog.csdn.net/weixin_39788856/article/details/94593902
  2. https://blog.csdn.net/weixin_39788856/article/details/94593656
  3. https://ask.csdn.net/questions/375009

JDK源码系列(2)-Object类相关推荐

  1. HashSet源码分析:JDK源码系列

    1.简介 继续分析源码,上一篇文章把HashMap的分析完毕.本文开始分析HashSet简单的介绍一下. HashSet是一个无重复元素集合,内部使用HashMap实现,所以HashMap的特征耶继承 ...

  2. JDK源码系列:Future是如何实现的?

    大家好,我们在异步编程时向线程池提交(submit)一个任务后会得到一个 Future对象,通过 future.get() 方法可以堵塞等待结果的完成,例如: public static void m ...

  3. JDK源码系列:子线程如何继承父线程上通过ThreadLocal绑定的数据

    上一篇中老吕介绍了ThreadLocal线程数据绑定的原理,今天聊聊父子线程之间如何继承ThreadLocal上维护的数据. 开发过程中异步执行任务有两种情况,第一种情况是 主线程 通过 new Th ...

  4. JDK源码系列:synchronized与wait、notify、notifyAll

    大家好,今天聊一聊synchronized与obj.wait().obj.notify().obj.notifyAll() 之间的关系以及它们的实现原理. 我们今天采用边写demo边分析的方式来进行. ...

  5. 大白话讲解JDK源码系列:从头到尾再讲一遍ThreadLocal

    引言 其实网上有很多关于ThreadLocal的文章了,有不少文章也已经写的非常好了.但是很多同学反应还有一些部分没有讲解的十分清楚,还是有一定的疑惑没有想的十分清楚.因此本文主要结合常见的一些疑问. ...

  6. JDK源码系列:ThreadLocal弱引用真的是过度设计吗?

    在<码处高效:Java开发手册>这本书上详细描述了ThreadLocal的原理,也有过度设计的说法, 难道弱引用设计真的没必要吗?对此老吕要仔细分析分析,ThreadLocal到底该不该使 ...

  7. JDK源码系列(3)-String

    在JDK中,String的使用频率和被研究的程度都非常高,所以接下来我只说一些比较重要的内容. 一.String类的概述 String类的声明如下: public final class String ...

  8. java object 源码_java中Object类 源代码详解

    packagejava.lang;public classObject {/*一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用.*/ private static native ...

  9. JDK源码系列:AQS(队列同步器)原理

    大家好,好久不见,今天看下JDK中的JUC包中AQS(AbstractQueuedSynchronizer 队列同步器)的实现原理. JUCL下的锁和synchronized提供的锁的区别 1.锁的获 ...

最新文章

  1. 腾讯扩招3000人,产品硬核技能被曝光,看完我跪了!
  2. Java核心技术及面试指南 异常部分的面试题归纳以及答案
  3. cpn tools查看运行时间_Jmeter在Linux下的运行测试
  4. 新工科背景下大数据专业导论课程的改革与探索
  5. mysql数据库as表恢复_【翻译】如何从ibdata和.frm文件恢复MySQL表数据
  6. Ubuntu 20.04 更新,界面美化及安装搜狗输入法
  7. 存储过程与业务类实现业务的差异比较
  8. java messagedigest,在C#中的Java MessageDigest类
  9. pid温度控制c语言程序,51单片机PID温度控制程序
  10. Pytorch显存分配机制与显存占用分析方法
  11. 还在为关闭不了win10杀毒软件windows defender而崩溃吗?小编三招教你如何彻底关闭
  12. Spring Security(安全)
  13. CodeForces - 616C The Labyrinth dfs+暴力
  14. 计算机控制技术直流电机调速控制实验报告,pid直流电机转速控制实验报告(31页)-原创力文档...
  15. OSI与TCP/IP协议簇、数据链路层
  16. ORAN专题系列-16:5G O-RAN FrontHaul前传接口的网络配置管理协议netconf
  17. 面经/字节跳动,面试流程及问题分享(附答案)
  18. 网络基础网络层--IP协议
  19. libpng 源码的使用 第四节:写 (接口)
  20. 从苏宁电器到卡巴斯基第23篇:难忘的三年硕士时光 I

热门文章

  1. 第七讲 虚拟机模板及大规模部署虚拟机
  2. [致歉]博客园升级造成的问题
  3. SpringCloud(三) Eureka注册中心介绍以及单机版搭建
  4. ZooKeeper(三) 什么是分布式锁以及使用Redis手写实现
  5. SpringBoot 第一篇入门
  6. ORACLE用SYS登录报ORA-28009:connection as SYS should be as SYSDBA OR SYSOPER解决方法
  7. 类加载机制、双亲委派机制深度解析以及如何自定义类加载器
  8. oracle 自动表分析,其实 Oracle 直方图自动统计算法存在这些缺陷!
  9. 泛型之类型擦除和桥接方法
  10. pytorch中的gather函数_Pytorch中Emdedding函数的解释及使用方法