JDK1.8-java.lang.Object类源码阅读

Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.

Object类是所有类的根基类,任何类都直接或间接继承自Object类。所有对象,包括数组都实现了Object类里的方法。

1、Object类的结构图

2、方法解析

2.1、构造方法

我们看到Object类里并没有显式的声明构造方法,所以系统会默认创造一个无参的构造方法

public Object(){}

2.2、equals方法

equals和 == 是老生常谈的问题了,== 用于比较基本类型的值是否相等或者比较两个对象的引用也就是内存地址是否相等,equals用来比较两个对象逻辑上是否相等,这个逻辑则是由程序员重写equals方法时制定。
Object 类中的equals 方法:

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

可以看到,在Object类里equals和 == 是等价的,所以如果我们不重写equals方法,则默认调用的是Object类里equals方法。
重写equals方法时需要满足以下原则:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

即:

  • 自反性:对于任何非空值x,x.equals(x)应该返回true。
  • 对称性:对于任何非空值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
  • 传递性:对于x,y和z的任何非空引用值,如果x.equals(y)返回true,而y.equals(z)返回true,则x.equals(z)应该返回true。
  • 一致性:对于任何非空引用值x和y,只要未修改对象的equals方法,对x.equals(y)的多次调用将始终返回true或始终返回false。
  • 对于任何非null参考值x,x.equals(null)应该返回false。

重写equals方法,通常必须重写hashCode方法,以维护hashCode方法的一般约定,该方法声明相等对象必须具有相同的哈希码。这点在jdk的API文档里也有描述:

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

2.3、hashCode方法

Object 类中的hashCode方法:

public native int hashCode();

该方法是一个本地方法,是操作系统帮我们实现的,用来返回对象的hash码,是int类型的。
在讲equals方法时提到,重写equals方法必须重写hashCode方法,那这是为什么呢?
要弄清楚这个问题,我们得知道什么是hash码,hash码也称为散列码,通过hash算法实现,将数据依hash算法产生的结果直接指定到一个地址上。那这和重写equals方法必须重写hashCode方法有啥关系呢?别急,往下看。
在java里有些容器,比如Set 存放的元素是无序不可重复的。要实现这个不可重复,那就得调用equals方法,如果数据量很大,比如有100000个数据,那每添加一个元素就得调用100000次equals方法,那效率可想而知。所以为了提高效率,引入了哈希表的实现。
这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,定位到属于它的位置。
如果该位置没有元素则直接插入。
如果该位置已经有元素了,则调用equals方法比较是否相等,如果相等就不存了。
如果不相等,就代表发生了hash冲突,就在该位置生成一个链表,将hashCode相同的对象放置在该链表上(在jdk1.8的hashMap实现里如果冲突的对象超过8个则将链表转为红黑树)
这样就大大提高了效率。
jdk的API文档里有对hashCode的要求:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

即:

  • 在程序运行期间,不修改equals方法时,对同一个对象多次调用hashCode方法必须返回一样的值
  • 如果两个对象根据equals方法判断相等,则他们的hashCode方法必须返回一样的值
  • 如果两个对象根据equals方法判断不相等,他们的hashCode方法有可能返回一样的值(hash冲突),hash冲突越少,hash表的性能就越好

我们可以将equals和hashCode的关系做一下总结:
两个对象相等,其 hashCode 一定相等
两个对象不相等,其 hashCode 有可能相等
hashCode 相同的两个对象,不一定相等
hashCode 不相同的两个对象,一定不相等

主意:对于Map集合,一定要选用重写了equals和hashCode方法的对象作为key,如果用自定义的对象作为key,则一定要重写equals和hashCode方法。

2.4、toString方法

Object 类中的toString方法:

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

getClass().getName()是返回对象的全类名(包含包名),Integer.toHexString(hashCode()) 是以16进制无符号整数形式返回此哈希码的字符串表示形式。
当打印某个对象时默认调用的就是toString方法,所以如果需要在日志里打印某个自定义的对象,那就得重写toString方法了,不然就是调用的Object类的toString方法,打印出来的信息也就没啥意义了。

2.5、clone方法

Object 类中的clone方法:

protected native Object clone() throws CloneNotSupportedException;

这也是一个本地方法,由操作系统实现。是复制一个对象,注意这里是“浅拷贝”不是“深拷贝”。如果要实现一个对象的“深拷贝”,则需要对象的所有引用类型的变量都实现clone方法,如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。
可以用序列化的方式来实现对象的深拷贝。这里不做展开。
一个类要想重写clone方法,必须实现Cloneable接口,否则会抛出CloneNotSupportedException。

2.6、getClass方法

Object 类中的getClass方法:

public final native Class<?> getClass();

这也是一个本地方法,作用是返回对象的运行时类。这也就是java里的反射,很多框架都会用到反射。
很多同学可能还见过xx.class,那这和getClass有什么区别呢?
class 是一个类的属性,能获取该类编译时的类对象,而 getClass() 是一个类的方法,它是获取该类运行时的类对象。
getClass常用来判断某个对象是否属于某个类的实例,instanceof也有同样的功能,那他们有什么区别呢?
做个实验,有如下代码:

public class Father {
}
public class Son extends Father{
}
public class Test {

    public static void test(Object obj){
        System.out.println("obj instanceof Father "+(obj instanceof Father));
        System.out.println("obj instanceof Son "+(obj instanceof Son));
        System.out.println("obj getClass Father "+(obj.getClass() == Father.class));
        System.out.println("obj getClass Son "+(obj.getClass() == Son.class));

    }
    public static void main(String[] args) {
        test(new Father());
        System.out.println("==========");
        test(new Son());
    }
}

输出为:

可见,instanceof判断时会考虑到继承的关系,子类instanceof父类也会为true,getClass()则不会考虑继承的关系

2.7、finalize方法

Object 类中的finalize方法:

protected void finalize() throws Throwable { }

该方法用于垃圾回收,一般由 JVM 自动调用,不建议程序员手动调用该方法,因为手动调用也不保证能立即执行。

2.8、notify,notifyAll以及wait方法

这些方法与多线程有关,这里就不做讨论了,简单几句也讲不清楚。

2.9、registerNatives方法

Object 类中的registerNatives方法:

private static native void registerNatives();

这也是一个本地方法,顾名思义,这方法是用来注册本地方法的。Object 类这么多的本地方法必须要装载本地库才能实现,下面的静态代码块在类加载的时候会执行registerNatives方法,从而装载本地库。

static {
        registerNatives();
    }

参考资料:

https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html

猜你喜欢

转载自www.cnblogs.com/liuwhut/p/13180727.html