JDK1.8源码阅读——Object类

1、Object类的结构java

    上图为Object类的结构树,由此能够清晰的看到整个Object的架构。其中我的通过搜索、平常开发的总结,认为Object、clone、equals(Object)、hashCode、getClass、toString这几个方法相对重要(仅属我的意见,若有不一样之见,欢迎讨论)。可能有人认为notify、wait等线程有关的方法也很重要,可是从我的角度出发,我认为这些方法更应该放在线程里去研究和讨论。程序员

2、native方法介绍面试

    咱们都知道,Java的底层是经过C、C++等语言实现的,那么Java是如何区分这些方法并能准确地去调用的呢?架构

2.1 native关键字ide

    在Java中,若是一个方法使用native关键字来修饰,即代表该方法并非由Java实现的,它是由non-java即C、C++负责实现。其具体的实现方法被编译在了dll文件中,由Java调用。由native修饰的方法有:测试


2.2  registerNatives()this

    registerNatives(),顾名思义,注册native修饰的方法,其做用是将C、C++等方法映射到Java中由native修饰的方法,这也是Java为什么能作到准确地去调用non-java方法。其源码以下:.net

private static native void registerNatives();
    能够看到,这里并无执行此方法。Java使用的是静态代码块去执行registerNatives(),源码以下:线程

static {
    registerNatives();
}
2.3   clone()code

protected native Object clone() throws CloneNotSupportedException;
    经过上述源码,咱们知道,clone不是Java原生的方法,且Object提供的复制是浅复制,不是深度复制。

    浅复制是指只复制对象的引用,而深度复制则是将原来复制的对象完彻底全的复制出来,此时被复制的对象与复制出来的对象已经没有任何关系,归纳起来就是,浅复制复制引用与值,引用与值都不变;深度复制复制值,引用变值不变。

    能够看到,clone方法显式抛出不支持复制的异常,这说明,实现对象的复制是有条件的。

    1)  方法由protected修饰,说明若要实现复制,须要继承Object(默认都是继承Object的...)

    2)返回类型为Object,代表若要获得咱们想要复制的结果须要进行类型转换

    3)实现Cloneable接口,不然会抛出不支持复制的异常

    如下为一段测试clone的代码:

class Son { 
    
    private Integer sonAge;
    
    private String sonName;
 
    public Son(Integer sonAge, String sonName) {
        super();
        this.sonAge = sonAge;
        this.sonName = sonName;
    }
 
    public Son() {
        super();
    }
 
    public Integer getSonAge() {
        return sonAge;
    }
 
    public void setSonAge(Integer sonAge) {
        this.sonAge = sonAge;
    }
 
    public String getSonName() {
        return sonName;
    }
 
    public void setSonName(String sonName) {
        this.sonName = sonName;
    }
}
public class Parent {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        
        Son son = new Son(10,"清然");
        
        Parent parent = new Parent(20,"安然",son);
        Parent temp1 = parent.clone();
    }
    
    private Integer age;
    
    private String name;
    
    private Son son;
    
    public Parent(Integer age, String name, Son son) {
        super();
        this.age = age;
        this.name = name;
        this.son = son;
    }
 
    public Parent() {
        super();
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Son getSon() {
        return son;
    }
 
    public void setSon(Son son) {
        this.son = son;
    }
    
}
   此时编译器提示须要进行类型转换:

   完成类型转换后,提示须要对异常进行处理,即CloneNotSupportedException:

    将异常抛出后,没有发现任何编译错误,咱们运行main方法,控制台出现以下错误:

Exception in thread "main" java.lang.CloneNotSupportedException: jdkreader.java.lang.object.Parent
    at java.lang.Object.clone(Native Method)
    at jdkreader.java.lang.object.Parent.main(Parent.java:16)
   异常显示Parent并不支持clone,此时咱们须要实现Cloneable接口:

public class Parent implements Cloneable
   咱们能够看一下复制后的两个对象的关系:

Son son = new Son(10,"清然");
        
Parent parent = new Parent(20,"安然",son);
Parent temp1 = (Parent) parent.clone();
Parent temp2 = (Parent) parent.clone();
        
System.out.println("temp1 == temp2 : " + (temp1 == temp2));
    运行结果为:

temp1 == temp2 : false
    很明显,结果为false,由于复制出来是一个新的对象,引用不一样。咱们再对他们的各个变量进行一一比较:

System.out.println("temp1Age == temp2Age : " + (temp1.getAge() == temp2.getAge()));
System.out.println("temp1Name == temp2Name : " + (temp1.getName() == temp2.getName()));
System.out.println("temp1Son == temp2Son : " + (temp1.getSon() == temp2.getSon()));
    运行结果为:

temp1Age == temp2Age : true
temp1Name == temp2Name : true
temp1Son == temp2Son : true
   很奇怪,他们的内部属性却所有是相同的,这是为何呢?

   由于Object提供的clone是浅复制,若是是基本类型,则复制其值,若是是引用内容,则复制其引用。因此二者指向的地址是相同的,故相等。

    2.4  getClass

public final native Class<?> getClass();
    此方法返回的是运行时类对象,这点从注释可明显看出:

Returns the runtime class of this {@code Object}.
    什么是类对象?咱们知道,在Java中,一切皆对象。在Java中,类是是对具备一组相同特征或行为的实例的抽象并进行描述,对象则是此类所描述的特征或行为的具体实例。做为概念层次的类,其自己也具备某些共同的特性,如都具备类名称、由类加载器去加载,都具备包,具备父类,属性和方法等。因而,Java中有专门定义了一个类,Class,去描述其余类所具备的这些特性。

  2.5  hashCode

public native int hashCode();
    这也是由non-java实现的方法,返回的是一个对象的散列值,类型为int。通常状况下,在当前程序的运行期间,一个对象屡次调用hashCode方法,其返回的散列值是相同的。这里须要注意的是:

    若两个对象相等,则它们的散列值必定相等,反之,散列值相等,两个对象不必定相等;

    若两个对象不相等,则它们的散列值不必定相等,反之,散列值不一样,两个对象必定不相等。

    在Java中,有许多地方都应用到了hash,如集合set、map,equals等,这里不作过多研究。

2.6  equals

public boolean equals(Object obj) {
     return (this == obj);
}
    关于“==”与equals的区别,面试的时候多数人都被问到过。咱们知道“==”比较基本数据类型时,比较的是值,当比较对象时,比较的是其引用;而equals比较的是两个对象是否相等。

    在Object类中,equals与“==”实际上是等价的,可是咱们在不少状况下是须要重写equals方法的,例如比较两个学生是否为同一我的,咱们直接使用equals方法确定是不可行的。一般状况下,咱们认为若是学号相同,那么这两个学生就是同一我的。重写的示例以下:

    Student类

/**
 * 用于测试重写Object类equals方法
 * 
 * @author xuyong
 *
 */
public class Student {
 
    private String no;        //学号
    
    private String name;    //姓名
 
    public Student(String no, String name) {
        super();
        this.no = no;
        this.name = name;
    }
 
    public Student() {
        super();
    }
 
    public String getNo() {
        return no;
    }
 
    public void setNo(String no) {
        this.no = no;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
    测试类

Student stu1 = new Student("1", "路人甲");
Student stu2 = new Student("1", "路人乙");
System.out.println(stu1.equals(stu2));
    运行,控制台打印false。此时,咱们重写一下Student类的equals方法:

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Student) {
            Student stu = (Student)obj;
            return no.equals(stu.getNo());
        }
        return super.equals(obj);    
    }
    再次运行刚刚的代码,控制台打印true。因而,咱们便实现了经过学号判断是否为同一我的的业务。

    不过,骚年们,觉得这就结束了吗?NO!

    因为Java须要维护hash的通常规律,没错就是刚刚标红的内容:两个对象相等,那么他们的散列值必须相等。

    可是此时,咱们测试一下他们的hash是否相等,能够明显发现,他们是不相等的。

System.out.println(stu1.hashCode() == stu2.hashCode());
    因此,咱们在重写equals方法时,必需要重写hashCode方法,因为咱们是经过学号判断的,因此最好也是使用学号的散列值替代原有的散列值:

    @Override
    public int hashCode() {
        return no.hashCode();
    }
    此时再运行方法,就会发现返回的是true了。

2.7  finalize

protected void finalize() throws Throwable { } 该方法用于垃圾回收,通常由 JVM 自动调用,通常不须要程序员去手动调用该方法。 ———————————————— 版权声明:本文为CSDN博主「t1heluosh1」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接及本声明。 原文连接:https://blog.csdn.net/PostersXu/article/details/81947715

相关文章
相关标签/搜索