「OpenJdk-11 源码-系列」 : Object

Object

在 Java 中 Object 类是全部类的祖先类,Object 没有定义属性,一共有13个方法。其它全部的子类都会继承这些方法。java

构造函数

registerNatives()

private static native void registerNatives();
 static {
        registerNatives();
 }
复制代码

在 Java 中,用 native 关键字修饰的函数代表该方法的实现并非在Java中去完成,而是由C/C++去完成,并被编译成了.dll,由Java去调用ios

registerNatives()方法的主要做用则是将C/C++中的方法映射到 Java 中的 native方法,实现方法命名的解耦。多线程

构造函数 public Object()

@HotSpotIntrinsicCandidate
 public Object() {}
复制代码

@HotSpotIntrinsicCandidate注解,该注解是特定于Java虚拟机的注解。经过该注解表示的方法可能( 但不保证 )经过HotSpot VM本身来写汇编或IR编译器来实现该方法以提供性能。 它表示注释的方法可能(但不能保证)由HotSpot虚拟机内在化。若是HotSpot VM用手写汇编和/或手写编译器IR(编译器自己)替换注释的方法以提升性能,则方法是内在的。 也就是说虽然外面看到的在JDK9中weakCompareAndSet和compareAndSet底层依旧是调用了同样的代码,可是不排除HotSpot VM会手动来实现weakCompareAndSet真正含义的功能的可能性函数

通常建立对象的时候直接使用 new className(Args) 来建立一个新的对象。而在类的定义过程当中,对于未定义构造函数的类,那么它就会默认继承Object的无参构造函数,若是定了一个或多个构造函数,那么就须要把无参构造函数方法也写上。性能

方法

public final native Class<?> getClass()

@HotSpotIntrinsicCandidate
 public final native Class<?> getClass();
复制代码

getClass返回运行时当前对象的类对象。在 Java 中,类是对具备一组相同特征或行为的实例的抽象进行描述。而类对象则是对的特征和行为进行描述(即类的名称,属性,方法...)。也就是说经过获取到类对象,则能够获取到该类的全部属性,方法等。this

public native int hashCode()

@HotSpotIntrinsicCandidate
public native int hashCode();
复制代码

hashCode 返回当前对象的哈希码。hashCode遵照如下三个约定spa

  • 在 Java 程序运行期间,对同一个对象屡次调用hashCode,那么它们的返回值须要是一致的。(前提:没有对对象进行修改)
  • 若是两个对象相等(调用equals()方法),那么这两个对象的 hashCode也是同样
  • 两个对象调用hashCode方法返回的哈希码相等,这两个对象不必定相等

也便是说,调用equals方法返回值相等,那么调用hashCode方法返回值也必定相等。因此,在重写euqlas方法以后,必定要重写hashCode方法。.net

那么判断对象是否先等能够直接用equals来判断,为何还须要hashCode方法呢?线程

其实hashCode方法的一个主要做用是为了加强哈希表的性能。好比:咱们知道Set集合不能存在相同的两个对象,那么该怎么判断两个对象是否相同呢?若是没有hashCode,那么就须要进行遍从来逐一判断。那么有hashCode,咱们就能够计算出即将要加入集合的对象的hashCode,而后查看集合中对应的位置上是否有对象便可。code

public boolean equals(Object obj)

public boolean equals(Object obj) {
   return (this == obj);
}
    
复制代码

equals()用于判断两个对象是否相等。根据 Object 的实现,能够看到判断的依据是看两个对象的引用地址是否相等。

而通常咱们会用另一种方式来判断是否相等。即==,==表示的是两个变量值是否相等(基础类型的值在内存地址中存储的是值)

那么咱们想要判断是否相等:

  • 若是是基础类型,就能够直接用==来判断
  • 若是是引用类型,那么就须要经过equals方法来判断(在实际业务中,通常会重写equals方法)

须要注意的一点是String也是引用类型,咱们判断String的时候是直接使用的equals方法,而按照默认的equals实现,建立两个具备相同值的String对象,那么equals返回的应该是false

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;
}
复制代码

public String toString()

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

toString()返回该对象的字符串表示。在使用 System.out.printLn(obj)的时候,其内部也是调用的toString方法。能够按需重写toString方法。

protected native Object clone()

protected native Object clone() throws CloneNotSupportedException;
复制代码

clone()方法返回的是当前对象的引用,指向的是新clone出来的对象,此对象和原对象占用不一样的堆空间。

clone方法的正确调用须要实现 cloneable 接口,若是没有实现该接口,那么子类调用父类的 clone方法则会抛出CloneNotSupportedException异常

Cloneable接口仅仅是一个表示接口,接口自己不包含任何方法,用来指示Object.clone()能够合法的被子类引用所调用。

1. 使用

先看一段代码

public class CloneTest {  
  
    public static void main(String[] args) {  
        Object o1 = new Object();  
        try {
            Object clone = o1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }  
  
}  
复制代码

执行这段段代码会抛出The method clone() from the type Object is not visible异常。缘由是clone 方法是被 protected修饰的,也就是说被protected修饰的属性和方法,在同一包下或在不一样包下的子类能够访问。显然,CloneTestObject不在同一包下,不过按照字面意思,CloneTest会默认继承Object,因此即便在不一样的包下,应该也是能够访问的才对。那么问题就出如今「在不一样包下的子类能够访问」这句话上:

不一样包中的子类能够访问: 是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。(super关键字除外)

也就是说在子类中想要调用父类的protected方法,能够

  • 在子类中重写父类的方法
  • 在子类中经过super.methodName()来调用父类方法

2. 浅拷贝&深拷贝

浅拷贝: 浅拷贝是按位拷贝对象,它会建立一个新对象,这个对象有着原始对象属性值的一份精确拷贝。若是属性是基本类型,拷贝的就是基本类型的值;若是属性是引用类型,拷贝的就是内存地址。 深拷贝: 深拷贝会拷贝全部的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一块儿拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢而且花销较大。

对于浅拷贝来讲,若是含有引用类型,那么修改其中一个对象的引用值,那么会影响到另一个对象。按层级来讲,浅拷贝只拷贝了第一层。对于默认的clone实现是浅拷贝。若是想要实现深拷贝,能够

  • 对对象进行序列化
  • 重写clone方法
//序列化实现深拷贝

public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            
            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

public class Person implements Serializable{
    private static final long serialVersionUID = 2631590509760908280L;
}

public class CloneTest {
    public static void main(String[] args) {
        Person person =  new Person();
        Person person1 =  CloneUtils.clone(person);     
    }
}


参考:https://blog.csdn.net/chenssy/article/details/12952063
复制代码

protected void finalize()

protected void finalize() throws Throwable {}
复制代码

finalize()方法主要与 Java 垃圾回收机制有关,JVM准备对此对形象所占用的内存空间进行垃圾回收前,将被调用。因此此方法并非由咱们主动去调用的。

wait()/notify/notifyAll

可先看java 多线程尝鲜。后续会专门讲多线程相关源码。

相关文章
相关标签/搜索