java.lang.Object源码分析

作者: Oliver_Li | 来源:发表于2019-12-02 11:47 被阅读0次

    1. 描述:

    • Object类是类层次结构的根。每个类都有 Object作为超类。所有对象,包括数组等等

    2. registerNatives()

    private static native void registerNatives();
    static {
       registerNatives();
    }
    
    • 私有的本地方法,使用JNI调用其他语言,根据不同的操作系统调用对应的底层操作。

    3. getClass()

    public final native Class<?> getClass();
    
    • getClass()返回对象的运行时Class对象
    • getClass()和 "类名.class" 都可以获取到Class对象,前者获得"运行时"的类对象,例如某对象多态情况下,实际对象为子类对象,这个对象调用getClass()获得的就是子类的class对象。后者则是类名的Class对象。如下代码。
    Parent p = new Son();
    System.out.println(p.getClass());    //Son
    System.out.println(Parent.class);    //Parent
    

    4. equals()

    public boolean equals(Object obj) {
         return (this == obj);
     }
    
    • 源码中Object的euqals()就是"=="("=="对于引用对象来说是比较在栈内存中的引用地址,基本类型比较值),但使用时大多场景需要重写euqals(),例如String和自定义的bean等等。
    • API中对equals()有如下描述(所有举例对象非null):
      • x.equals(x)应该返回true
      • x.equals(y)返回true时,当且仅当y.equals(x)返回true
      • 如果x.equals(y)返回true、y.equals(z)返回true ,则x.equals(z)应该返回true
      • 如果比较对象没有被修改,多次调用 x.equals(y) 始终返回 true 或始终返回 false
      • 对于任何非空的参考值x , x.equals(null)应该返回false
    • 重写equals()必须重写hashCode()!

    5. hashCode()

    • 返回对象的哈希码值(只是一个native方法源码就不贴出来了)。
    • 集合类例如HashMap、HashTable中常借助哈希表来判断对象是否重复,避免全集合遍历扫描。最常见如HashMap放入数据就是取hashCode()然后根据map容量取余定位到具体的桶(bucket),如果桶里已有数据(hash冲突)则通过equals()判断是否相等,不相等就尾部添加变成链表,链表到达一定长度变成红黑树,以此进一步优化执行时间。
    • API中对该方法描述:
      • 在不影响equals()比较信息的情况下,多次调用hashCode()应返回相同整数
      • equals()相等的两个对象,hashCode()的hash码必须相同(如果重写equals()不重写hashCode()会出现equals()相等hashCode()不等判断不出对象唯一的情况)
      • equals()不相等的两个对象,也有可能hash码相同
    • 从API中可得知在没被重写的情况下equals()相等(可以认为是同一对象),hashCode()一定相等,hashCode()相等对象不一定相等。不一定的原因是因为hashCode()是固定32长度的整形数字,对象可以随意生成肯定会比这个数字多,不可能一个对象对应一个hash码,但是量很少时hash码可以认为对象唯一,后面通过这个方法可以推测对象生成的情况,源码很多重写了hashCode(),可以借助System.identityHashCode()调用Object的hashCode()。

    6. toString()

    • 返回对象的字符串表示形式。
    • 这句话API说得很从抽象,其实就是类的全路径 + @ + 16进制的hash码,源码很短直接贴出来了,如下:
      //java.lang.Object@731a74c
      return getClass().getName() + "@" + Integer.toHexString(hashCode()); 
      

    7. clone()

    • 创建并返回此对象的副本。
    • 这里涉及到两个概念"深拷贝"和"浅拷贝",常用类型有两种"引用类型"和"基本类型"。
      • 引用类型:常见的自定义bean、数组等
      • 基本类型:boolean、byte、char、short、int、float、long、double
    • 引用类型存在栈,具体内容存在其他地方,例如堆。这样就出现个问题,引用类型如果只把栈的引用地址copy过去(浅拷贝Object.clone()),改变源数据就会导致所有引用的地方全部修改,如果能把源数据也copy一份的完全不影响这样就可以了(深拷贝,通常要重写clone()或反序列化实现)。

    8. wait()

    • API解释:导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。当前的线程必须拥有该对象的monitor。
    • wait()、notify()、notifyAll()、是一组组合使用的方法,他们都需要获取对象的monitor(代码中常见就是各种锁,如synchronized)时才可以调用,释放锁有三种常见方式:
      • 执行完同步代码块,自动释放。
      • 运行同步代码块时遇到异常终止,异常释放。
      • 运行同步代码块时,内部调用wait(),线程进入等待池。
    • wait()释放锁的线程会进入等待池,必须经过其他线程调用notify()命中这个线程或notifyAll()才会继续和其他线程竞争执行机会,否则会一直等待,没权力竞争cpu调度。
    • API中提到了一个写法问题,所有wait()和重载方法应该写在while循环中,循环条件是是否达到继续执行的条件,防止逻辑错误导致执行了不想执行的wait()线程。

    9. wait(long timeout)

    • wait()的重载方法,功能类似,只是设置一个timeout,超时后会自行唤醒,进而竞争cpu调度,当然如果等待期间notify()命中或notifyAll()也会激活。

    10. notify()

    • 如果等待池有线程,则随机唤醒其中一个
    • 唤醒等待线程后不会立即释放当前锁,会执行完notify()所在的方法后才会释放锁,其他有权竞争执行的线程才会竞争。

    11. notifyAll()

    • 唤醒等待池所有线程。

    12. finalize()

    • 当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。 一个子类覆盖了处理系统资源或执行其他清理的finalize方法。
    • 这个方法是在GC处理该对象时会调用的方法,通常可以重写finalize()做一些对象回收时的业务逻辑,一般很少用到。

    相关文章

      网友评论

        本文标题:java.lang.Object源码分析

        本文链接:https://www.haomeiwen.com/subject/tuyxgctx.html