Java之Object类源码实现

一、引言

我们知道Java是单继承语言,所有类的最终父节点都是Object(java.lang.Object)类,这一点与C++不同,因为C++既可以单继承也可是多继承。上述的所有类包括数组这些等。

二、分析

1.结构与源码

在这里插入图片描述

public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }
	
	// 获取对象的类型
    public final native Class<?> getClass();
	
	// 获取HashCode的方法
    public native int hashCode();
	
	// 对象的比较方法
    public boolean equals(Object obj) {
        return (this == obj);
    }
	
	// 对象的克隆的方法
    protected native Object clone() throws CloneNotSupportedException;

	// 多线程中唤醒单个线程的方法
    public final native void notify();

	// 多线程中唤醒所有线程的方法
    public final native void notifyAll();
    
    // 多线程中线程的等待
    public final native void wait(long timeout) throws InterruptedException;
	
	// 多线程中的线程等待
    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);
    }
	
	// 多线程中线程等待的方法
    public final void wait() throws InterruptedException {
        wait(0);
    }
	
	// GC回收前执行的方法
    protected void finalize() throws Throwable { }
}

注意:
1.被native修饰的方法,都是由本地实现的,通过JNI调用C或C++操作系统底层
native修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
2.关于Object中的具体每一个方法的作用我们将在下面仔细介绍,上面的注释就是帮助大家对其有个初步的认识

2.分析

2.1 registerNatives()方法
	// 这是native的方法
	// 我们知道:
	// ①native的方法有本地的其他语言或操作系统来实现;
	// ②与此同时还要不在此类中装载此类;
	// ③由于被private修饰,不能被类的外部调用。所以会在Object类加载时,通过其来注册native方法
	// 作用:向Object类注册本地实现的方法。
    private static native void registerNatives();
    static {
        registerNatives();
    }

2.2 getClass()方法
	// 本地实现的方法
	// 作用:返回一个类运行时的类型
	public final native Class<?> getClass();

思考:此方法和ClassType.class获取的Class对象有什么区别?
例子:

public class ObjectDemo {

    public static void main(String[] args) {
        Fu fu = new Zi();
        System.out.println(fu.getClass());
        System.out.println(Fu.class);
    }

    static class Fu {}
    static class Zi extends Fu {}
}

运行结果如下:
在这里插入图片描述

区别:getClass获取的运行时类型;xxx.class获取的是编译时类型

2.3 hashCode()方法
	// 作用:返回对象的散列码,是 int 类型的数值
	// 尤其是在集合这块体现的
	public native int hashCode();

HashCode的三个原则:

  1. 在程序运行时期间,只要对象的(字段的)变化不会影响equals方法的决策结果,那么,在这个期间,无论调用多少次hashCode,都必须返回同一个散列码。
  2. 通过equals调用返回true 的2个对象的hashCode一定一样。
  3. 通过equasl返回false 的2个对象的散列码不需要不同,也就是他们的hashCode方法的返回值允许出现相同的情况。
    关于HashCode的更多参考这里:待完善
2.4 equals(Object obj)方法
	// 作用:返回两个对象是否相等
    public boolean equals(Object obj) {
        return (this == obj);
    }

看到这里,很自然的联想到 “= =”和equal()的区别是什么。通常,==用于基本类型的值是否相等或比较两个对象类型的引用是否相同。而equals用于比较两个对象是否相同,但是怎么判断这两个对象是否相同,定义是很宽泛的。
从Object类的源码中可以看到:Object中 == 和equals方法是等价的。
所以,如果我们自定义对象,如果不重写equals方法时,则调用的是Object的equals方法。如String中的equals的方法:

    public boolean equals(Object anObject) {
        if (this == anObject) {
        	// 如果地址引用相等,则这两个对象一定相等
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            // String的长度相等的情况下
            if (n == anotherString.value.length) {
            	// 逐个字符进行比较,只要有一个不相等,就直接返回false。否则就是true。
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        // 如果不是String类型,直接返回false
        return false;
    }

由此我们看出String重写了equals方法:其比较的是两个字符串的内容。
Java中,关于equals方法有以下几个必须遵循的原则:

  1. 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true;
  2. 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true;
  3. 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true;
  4. 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改;
  5. 对于任何非空引用值 x,x.equals(null) 都应返回 false
    下面我们定义一个测试类:来检测下。
public class ObjectDemo {

    public static void main(String[] args) {
        Person p1 = new Person("1001", "tom");
        Person p2 = new Person("1001", "tom");
        System.out.println(p1.equals(p2)); // true
    }

    static class Person {
        private String id;
        private String name;
        public Person() {}
        public Person(String id, String name) {
            this.id = id;
            this.name = name;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object obj) {
            // 地址值相等,一定相同
            if (this == obj) {
                return true;
            }
            // 为null或不是Person类型,一定不相同
            if (obj == null || !(obj instanceof Person)) {
                return false;
            }
            Person p = (Person) obj;
            // 只有当id和名字都相等,这两个对象才算相同
            if (p.getId().equals(this.id) && p.getName().equals(this.name)) {
                return true;
            }
            return false;
        }
    }
}

从上面代码可以看出:我们定义Person相等的尺度是地址值相等或id、名字相等才算相同。
这时看起来,我们重写的equals方法很完美啊。但是当我写一个学生类(Student)继承Person类时,也重写了equals方法,错误就出现了。

    public static void main(String[] args) {
        Person p1 = new Person("1001", "tom");
        Student s = new Student("1001", "tom", "909");
        System.out.println(p1.equals(s)); // true
        System.out.println(s.equals(p1)); // false
    }

    static class Student extends Person {
        private String classRoom;

        public Student() {}

        public Student(String id, String name, String classRoom) {
            super(id, name);
            this.classRoom = classRoom;
        }

        @Override
        public boolean equals(Object obj) {
            // 地址值相等,一定相同
            if (this == obj) {
                return true;
            }
            // 为null或不是Person类型,一定不相同
            if (obj == null || !(obj instanceof Student)) {
                return false;
            }
            Student s = (Student) obj;
            // 只有当id和名字及classRoom都相等,这两个对象才算相同
            if (super.getId().equals(s.getId()) && super.getName().equals(s.getName()) && this.classRoom.equals(s.getClassRoom())) {
                return true;
            }
            return false;
        }

        public String getClassRoom() {
            return classRoom;
        }

        public void setClassRoom(String classRoom) {
            this.classRoom = classRoom;
        }
    }

运行结果为:
在这里插入图片描述
通过打印结果:Person.equals(student)为true,但是Student.equals(person)为false。这违反了equals的对称性。
其实问题出现在instanceof关键字的判断的那句。关于instanceof的用法可以参考这篇文章,总结的很好。
Student是 Person 的子类,person instanceof Student结果当然是false。这违反了对称性原则。
实际上用 instanceof 关键字是做不到对称性的要求的。这里推荐做法是用 getClass()方法取代 instanceof 运算符。getClass() 关键字也是 Object 类中的一个方法,作用是返回一个对象的运行时类,具体如何使用我们将会在下面进行介绍。

public class ObjectDemo {

    public static void main(String[] args) {
        Person p1 = new Person("1001", "tom");
        Student s = new Student("1001", "tom", "909");
        System.out.println(p1.equals(s)); // true
        System.out.println(s.equals(p1)); // false
    }

    static class Person {
        private String id;
        private String name;
        public Person() {}
        public Person(String id, String name) {
            this.id = id;
            this.name = name;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object obj) {
            // 1.地址值相等,一定相同
            if (this == obj) {
                return true;
            }
            // 2.为null,一定不相等
            if (obj == null) {
                return false;
            }
            // 3.检测是否是一个类,如果不是一个类直接返回false
            if (getClass() != obj.getClass()) {
                return false;
            }
            // 4.如果不是Person类型,一定不相同
            if ( !(obj instanceof Person)) {
                return false;
            }
            // 5.将obj转为对应的类型
            Person p = (Person) obj;
            // 6.对对象的属性进行比较,== 基本类型 equals是对象类型
            if (p.getId().equals(this.id) && p.getName().equals(this.name)) {
                return true;
            }
            // 6-2.如果是在子类,子类的equals方法要包含父类的equals方法。如在子类Student中
            return false;
        }
    }

    static class Student extends Person {
        private String classRoom;

        public Student() {}

        public Student(String id, String name, String classRoom) {
            super(id, name);
            this.classRoom = classRoom;
        }

        @Override
        public boolean equals(Object obj) {
            // 1.地址值相等,一定相同
            if (this == obj) {
                return true;
            }
            // 2.为null,一定不相等
            if (obj == null) {
                return false;
            }
            // 3.检测是否是一个类,如果不是一个类直接返回false
            if (getClass() != obj.getClass()) {
                return false;
            }
            // 4.如果不是Person类型,一定不相同
            if ( !(obj instanceof Student)) {
                return false;
            }
            // 5.将obj转为对应的类型
            Student s = (Student) obj;
            // 6.对对象的属性进行比较,== 基本类型 equals是对象类型
            return super.equals(s) && this.classRoom.equals(s.getClassRoom());
            // 6-2.如果是在子类,子类的equals方法要包含父类的equals方法。如在子类Student中
        }

        public String getClassRoom() {
            return classRoom;
        }

        public void setClassRoom(String classRoom) {
            this.classRoom = classRoom;
        }
    }
}

改造后的打印结果均为:
在这里插入图片描述
注意 :

  1. 重写equals方法的时候,都要重写equals方法,以保持其统一性。
  2. 当然getClass()的这种比较方式不是唯一性的,我们要根据自己业务的情况来定义定义对象是否相等的标准。比如说java中集合类,即使getClass这种方式也不太合适,这种类不适合指定相等的标准。
2.5 clone()方法
	   /**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but these are not absolute requirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
	protected native Object clone() throws CloneNotSupportedException;

我们可以看出这是一个本地实现的方法。其作用:创建并返回此对象的一个副本。
思考:

  • 为什么要有克隆?
  • Java中的克隆是怎么样的?
  • Object中的clone的方法与Cloneable接口的关系?
  • 深度拷贝和浅度拷贝的区别是什么?
    关于这些疑问?请看这篇文章写得很好。我就不赘述了。Cloneable接口和Object的clone()方法
2.6 toString()方法
	// 可以看出toString打印的是类运行时的类名 加上 对象的hash码十六进制表示。
	// 以此来表示其在内存中地址值。
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
2.7 notifyAll()、wait(long timeout)、wait()等方法

详细参考线程系列文章多线程系列文章

2.8 finalize()方法

作用:用于垃圾回收,一般由 JVM 自动调用,一般不需要程序员去手动调用该方法。一般会在对象被垃圾回收器清理前调用此方法。

三、结语

  1. 学习Object类,有利于我们进一步的理解OOP的思想。
  2. 重点理解equals、clone等方法的使用和意义。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值