Object源码剖析(待完善)

本文详细探讨了Java中Object类的织入时机,解析了JDK6及7之后的变化。深入分析了Object类的主要方法,包括hashCode、equals、wait等,解释了它们的实现原理和作用。同时,文章讨论了equals和hashCode的重写规则及其在对象比较和集合操作中的重要性。此外,还涉及了对象内存地址、 hashCode生成策略以及其在不同JVM版本中的差异。
摘要由CSDN通过智能技术生成

Object:

一. Object类是何时被织入其他的类中的

问题来源 : java中所有的类都可以直接调用Object类中的方法,IDEA在开发过程中就可以直接编码调用.我们也说Object是所有类的父类,但是代码中我们并没有extends Object ,那Object是何时织入的呢:

可能的答案: A. javac 编译进去的 B.jvm中执行时织入的 C.开发工具如何做到的???

JDK 6(含)之前是 编译器 处理, JDK 7(含)之后是虚拟机 处理. 我们可以用反汇编证明这一结果,用javap执行 *.class 获得反汇编的代码观察类是否继承了Object即可

结论:

1、JDK 6(含)之前是 编译器 处理

2、JDK 7(含)之后是 虚拟机 处理

3、IDEA里面是开发工具做了智能处理

二.Object类主要方法源码剖析 (jdk8)

package java.lang;

public class Object {
	// native方法,对象初始化自动调用
    private static native void registerNatives();
    static {
        registerNatives();
    }
    // native方法
    public final native Class<?> getClass();
	// native方法
    public native int hashCode();
	
    public boolean equals(Object obj) {
        return (this == obj);
    }
	// native方法
    protected native Object clone() throws CloneNotSupportedException;
	
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
	// native方法
    public final native void notify();
	// native方法
    public final native void notifyAll();
    // native方法
    public final native void wait(long timeout) throws InterruptedException;
	// 最终调用wait()
    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 > 0) {
            timeout++;
        }

        wait(timeout);
    }
	// 最终调用wait()
    public final void wait() throws InterruptedException {
        wait(0);
    }
	
    protected void finalize() throws Throwable { }
}

可以看出调用本地方法栈 7个方法(调用底层C++方法) , java方法 5个

1.public native int hashCode();

1.hashCode是什么: hashCode是通过调用native方法中C++代码的一个 get_next_hash()方法获取的,是马萨利亚教授写的xor-shift 随机数算法(异或随机算法) ,因此问题2答案是:否

2.hashcode是不是内存地址: 否

3.hashcode存在哪里: 在对象头里,入下图. Mark Word中存储的就有hashCode.
java对象模型

其中,Mark Word的结构如下
Mark Work模型

从上图可以看出:

A.无锁状态:hashCode存放在Mark Word中

B.轻量级锁状态:Mark Word 存储的是一个锁记录的指针,指针指向C中的的 helper,从中可以获取到hashCode

C.重量级锁状态:Mark Word 存储的是一个锁记录的指针,指针指向C中的的 monitor,从中可以获取到hashCode

D.GC状态:GC结束后再根据锁状态获取hashCode,不会发生改变

E:偏向锁状态:一旦调用hashcode,偏向锁将被撤销,回到A状态(参考 https://blog.csdn.net/wo541075754/article/details/115497869)

4.hashcode是否会发生改变: 可以从两方面去分析. 第一:有很多类需要用到hashCode进行一些 相等比较或者 计算索引下标,例如 hashSet,hashMap等,如果同一个对象的hashCode发生了改变,那对象的属性就会发生改变,肯定是不允许的 第二:hashCode的计算其实是基于算法实现.当hashCode方法未被调用时,object header中用来存储hashcode的位置为0,只有当hashCode方法首次被调用时,才会计算对应的hashcode值,并存储到object header中。当再次被调用时,则直接获取计算好hashcode即可。 因此即使发生了GC,对象的内存地址发生了改变,hashCode要么还没计算出来,要么计算出来已经存好不会发生改变.

// hashcode生成的方式
不同的JVM对hashcode值的生成方式不同。Open JDK中提供了6中生成hash值的方法。

0:随机数生成器(A randomly generated number.);
1:通过对象内存地址的函数生成(A function of memory address of the object.);
2:硬编码1(用于敏感度测试)(A hardcoded 1 (used for sensitivity testing.));
3:通过序列(A sequence.);
4:对象的内存地址,强制转换为int。(The memory address of the object, cast to int.5:线程状态与xorshift结合(Thread state combined with xorshift);
其中在OpenJDK67中使用的是随机数生成器的(第0种)方式,OpenJDK89则采用第5种作为默认的生成方式。所以,单纯从OpenJDK的实现来说,其实hashcode的生成与对象内存地址没有什么关系。而Object类中hashCode方法上的注释,很有可能是早期版本中使用到了第4种方式。

5.hashcode什么时候产生: 当hashCode方法首次被调用时

6.hashcode有什么用:

​ A:用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的

​ B:用于比较两个对象是否相同,和equals结合使用

7.hashcode值是唯一的吗: 不是唯一,因此hashCode相同的对象不一定相同,也是因此很多业务场景需要重写hashCode()和 equasl()两个方法

2.public boolean equals(Object obj){}

1.equals 比较的是什么

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

​ 从上面Object.equals() 方法中可以看出,未重写equals方法时,java对象的equals方法即调用 ==

2.什么时候要重写equals方法

​ 简单来说,我们可以按照自己的需求去重写equals方法,拿String.Class 举例

public boolean equals(Object anObject) {
    	// == 比较内存地址相同,返回true
        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;
 }

3.什么时候要重写hashCode方法

​ hashCode是给java集合类的一些动作提供支撑,来判断俩对象“是否是同一个”的标准,例如hashSet,hashMap等

4.为什么推荐equals和hashCode方法都要重写

​ 有时候我们业务需要重写eqals方法,但是有的框架底层用hashCode实现了一些比较,为了保持一致性需要两个都重写

5.关于== 比较

​ 用于基本数据(8种)类型(或包装类型)相互比较,比较二者的值是否相等。

​ 用于引用数据(类、接口、数组)类型相互比较,比较二者地址是否相等。

public class Intern { 
    public static void main(String[] args) { 
        String str1 = "张三"; // 字符串常量池 
        String str2 = new String("张三"); // 堆内存
        System.out.println("str1与str2是否相等>>" +(str1==str2)); // false(堆内存地址和字符串常量池地址进		行比较) 
        System.out.println("str1与str2是否相等>>" +(str1==str2.intern())); // true(String.intern()指向			了字符串常量池) 
    } 
}

​ 特殊情况, Value.of()方法:

​ 基础类和包装类进行比较时,会进行拆箱操作,实际比较的是值;

​ 但是 Integer.value()方法比较特殊

public static Integer valueOf(int i) {
    // -128~127之间会从cache直接获取,超出这个范围的会new Intger(),因此 
    // Integer(127) == Integer(127) 是ture, Integer(128) == Integer(128) 是false
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)]; 
    return new Integer(i); 
}


未完待续…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值