JDK源码解析--Object类

        作为一名java开发,肯定会知道object类,object类是所有类的基类,当一个类没有直接继承任何类时,默认继承object类,所以也被称之为“上帝类”。

目录

一、继承Object的时机

二、类中方法解析

 1.类构造器

 2.equals方法

3.hashcode方法

4.clone方法

5.getClass方法

6.toString方法

7.finalize方法

8.registerNatives方法


一、继承Object的时机

        由此,我们抛出一个问题,object类时什么时候成为一个类的父类的?是编译时还是虚拟机处理的时候。

        这里我们通过JDK自带的反编译工具javap来看一下就明白了

可以看到在编译时期就已经继承了object类

二、类中方法解析

这是object类中方法,其中共有7个native方法,也就是说这七个方法并不是java本身去完成的,而是通过c/c++来完成,放在.dll动态文件中

关于如何通过java语言调用c/c++的代码,则是有标准的JNI协议来定义的,感兴趣可以自行去了解一下

 1.类构造器

每个类都会有类构造器,如果没有声明,则会默认创建一个无参构造

 2.equals方法

这个方法相信大家再熟悉不过了,面试中也是一个高频问点,比如经典的:

  • “equals()和==有什么区别”
  • “有没有重写过equals”
  • “什么情况下需要重写equals()以及hashcode()”

在Object中的equals()其实跟==是一样的,都是比较两个对象的引用是否相等,也就是说,如果两个对象的引用相等,那么它俩的对象一定是相等的

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

        那肯定就有人有疑惑了,那为啥String当中的equals()也会去比较值是否相等呢,那正是因为String对equals()进行了重写。先是比较了引用,然后再遍历比较值是否相等(下图为jdk11的代码,如果是jdk8则不会将这个方法拆开分情况处理)

 public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String aString = (String)anObject;
            if (coder() == aString.coder()) {
                return isLatin1() ? StringLatin1.equals(value, aString.value)
                                  : StringUTF16.equals(value, aString.value);
            }
        }
        return false;
    }

@HotSpotIntrinsicCandidate
    public static boolean equals(byte[] value, byte[] other) {
        if (value.length == other.length) {
            for (int i = 0; i < value.length; i++) {
                if (value[i] != other[i]) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

所以,当我们自定义一个类时,如果这个类不重写equals方法,那么只会比较引用,并不会去比较值,值得注意的是,重写equals方法通常都必须重写hashcode方法,以维护hashcode方法的一般约定,该方法声明相等对象必须具有相同的哈希代码

3.hashcode方法

该方法在object中为native方法,作用就是返回对象的散列码,是int类型的数值

 HashCode的存在主要是为了查找的快捷性,用来在散列存储结构中确定对象的存储地址,比如说,我们判断一个元素是否相等可以通过equals方法,但是如果集合中有10000个元素,当我们添加一个新元素时,就要调用10000次equals来比较,显然效率很低。

于是java的集合设计者就采用了哈希表来实现,就是将数据依照特定的算法产生的结果指定到一个地址上,这样当集合需要添加新元素的时候,调用其hashcode,就能计算出应该将其存放到什么位置上。

那么由此产生了几种情况:

  1. 该位置没有元素,则直接存放
  2. 该位置有元素,调用equals进行比较,如果相同则无需再存储
  3. 如果不相同,也就是意味着发生了哈希冲突,那这个时候则会在该位置产生一个链表,将产生相同的hashcode存放到这个链表中(如果看过HashMap源码的话就知道了) 

那么hashcode到底是啥,是否为对象的内存地址?

要清楚这一点我们需要知道,内存地址是不可能重复的,所以只要产生hashcode重复的情况就能证明hashcode不为内存地址

public class HashCode_Test {
    public static void main(String[] args) {
        int num =0;
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 200000; i++) {
            Object o = new Object();
            if(list.contains(o.hashCode())){
                num++;
            }else{
                list.add(o.hashCode());
            }
        }
        System.out.println("重复hashcode个数为:"+num);
        System.out.println("不重复hashcode个数为:"+list.size());
    }
}
重复hashcode个数为:8
不重复hashcode个数为:199992

可以看到在循环20万次时,会出现重复的情况,所以先是佐证了hashcode并不是内存地址

那么hashcode的存储位置又在哪里呢?

这个时候我们需要先了解一下啊对象的内存布局

        hashcode存放在java的对象头当中,对象头分为两个部分,一个是MarkWord(标记字段),另一个是Class Pointer(类型指针)。其中Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。、

下图为MarkWord的机构

当一个对象处于无锁状态时,会用31个bit来存储hashcode;当该对象有锁无论是轻量级锁、重量级锁都没有存放hashcode的空间,因为被指针占据了

如果在偏向锁的过程中,调用了hashcode方法,则会先撤销偏向锁,因为存储hashcode的bit被线程id占据了,所以得先撤销再去生成hashcode

如果有线程硬性调用对象的锁,则对象再也回不到偏向锁状态,而是升级为重量级锁,hashcode跟随mark word被移动到c的object monitor中

4.clone方法

该方法时实现对象的浅拷贝,也就是说只是将指针指向了该对象,而深拷贝则是重新创建了一个一样的对象

值得注意的是,被拷贝的对象需要实现Cloneable接口才行

5.getClass方法

作用是返回对象的运行时类 

6.toString方法

作用是返回对象的全类名+十六进制的hashcode,我们打印某个类时,就是默认打印的该类的toString方法

7.finalize方法

当GC确定不再有对该对象的引用时,GC会调用对象的finalize方法来进行回收,jvm会确保一个对象的finalize方法只会被调用一次,并且程序中不能直接调用它

8.registerNatives方法

一个类如果要调用操作系统的实现,则必须要装载本地库,由于Object中用了static代码块调用该方法,所以在类加载时必定会执行该方法


以上就是Object的源码解析,重点还是在hashcode以及equals那部分

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值