一、JDK源码
作为Java中所有Class的爸爸,Object类无疑是Java中非常重要的一个类,今天我们就直接来阅读以下JDK源码中的Object类,对其中的代码进行解析。
首先,废话不多说,我们直接上源码:
1 | package java.lang; |
二、Object类
看到上面的代码,是不是非常惊讶,作为Java中的基类,Object的源码似乎也并不是太复杂。
是的,你没有感觉错误,其实只要你打开JDK的源码来看一看的话,就会发现,在Object类中,除去那些一大堆的看不懂的英文注释之外,所有的有效代码就是上面的那些。
Object类是java.lang包下的一个Class,从写法上来看,它和我们平常自己写的类也没有太大的区别,那么Object类作为java中的基类有什么特殊之处呢,那就让我们来解析一下吧。
1、所有的类都继承自Object类
首先,Object类作为Java的基类,Java中所有的类(包含JDK中的和我们自己定义的)都会显式或隐式继承Object类,所以Object类是Java中所有类的父类,或者是超父类。故此,我们也将Object类称作基类,或者是超类。
2、Object类中的方法
既然Object类是基类,那么根据Java中继承的特性,也就是所Java中除了Object类之外的所有类都是Object类的子类,也就是说Object类中的所有的非私有方法都会被其子类继承。
所以,Java中的任意一个类,都可以调用Object类中的方法。
Object类中的方法一共有13个,1个构造方法,1个私有方法,2个受保护方法,其余9个则是公有方法。
而今天需要详细解析的也就是这13个方法。
三、Object中的方法
1、Object()
Object类的构造方法在JDK源码中并没有直接书写出来,是一个默认的无参构造方法,可以直接通过关键字new来进行创建一个Object类型的对象。
例如:
1 | Object object = new Object(); |
2、registerNatives()
1 | private static native void registerNatives(); |
这个方法是private的,是私有的,所以这个方法是Object类独有的方法,不会被其子类继承。
registerNatives() 方法独特的地方在于,使用了 native关键字进行修饰,在java中,使用native关键字进行修饰,那就说明这个方法的底层实现,并不是由Java代码来进行编写的,而是调用了其他语言编写的接口来进行实现的。
我们可以从源码中发现:
1 | static { |
在Object类中有一个静态代码块,直接调用了这个方法,也就是说,这个方法是所有的类进行实例化的时候都会执行的方法。
这个方法和Java语言的底层实现有关系,我们只需要了解即可,并不是今天的重点。
3、clone()
1 | protected native Object clone() throws CloneNotSupportedException; |
clone()方法是一个受保护的方法,作用是克隆一个对象,我们可以实例化一个Object类的对象,看看Object对象可以直接调用哪些方法:
从上面的图我们可以发现,其实Object类的实例对象竟然也无法调用clone()方法,那么Java中该怎么调用clone()方法呢?
方法很简单,实现Cloneable接口,并覆写clone()方法即可!
我们先来写一个User类,并让User类实现Cloneable接口,并覆写clone()方法。
1 | public class User implements Cloneable{ |
我们写好了User类,并让User类实现了Cloneable接口和覆写clone()方法,那么我们来实例化一个User对象。
从上面的图我们可以看见,除去User类自己的6个setter和getter方法之后,User类可以调用的方法有10个,其中就包括之前无法调用的clone()方法。
说完了实现,我们在说说clone方法的结果。
1 | User user1 = new User(); |
执行上面的代码,得到结果:
1 | false |
从这个结果我们可以看出,其中clone()方法克隆出来的对象,和原来的对象并不是同一个,而是从新创建了一个新的对象。
4、finalize()
1 | protected void finalize() throws Throwable { } |
finalize()方法是一个和Java的垃圾回收机制有关的方法。
这个方法会在Java的垃圾回收机制回收对象所占内存之前被调用,即当一个对象被Java虚拟机宣告死亡时会先调用它finalize()方法,让这个对象处理它被回收前的最后事情。
如果我们在Java类中覆写了该方法,可以通过覆写的方法,摆脱本身被Java垃圾回收机制回收的命运。
对于这个方法,我们只限于了解,并不深究。
5、getClass()
1 | public final native Class<?> getClass(); |
接下来要说的是9个公有方法,也是Java类中最为常用的9个方法。第一个就是getClass()。
通过这个方法,可以获取Java对象的字节码对象,具体操作如下:
1 | User user = new User(); |
6、hashCode()
1 | public native int hashCode(); |
hashCode()方法,可以获取Java对象的哈希码值,具体操作如下:
1 | User user = new User(); |
运行结果如下:
1 | 366712642 |
7、equals(Object obj)
1 | public boolean equals(Object obj) { |
equals(Object obj)方法,其实从源码不难发现,equals方法最底层还是调用了==运算符来实现的,也就是说Object类的equals方法和双等号运算符并没有什么区别。
也就是基本数据类型比较值是否相等,应用数据类型比较内存地址是否相等。
而String类型的equals方法之所以可以比较应用数据类的值是否相等,那是因为String类中重写了equals方法。
1 | User user1 = new User(); |
结果如下:
1 | true |
8、toString()
1 | public String toString() { |
toString()方法,在JavaBean中经常被覆写的方法,用于返回对象的字符串表示形式。
1 | User user = new User(); |
没有被覆写时结果为:(包名+引用内存地址)
1 | com.day01.object.User@15db9742 |
当toString()方法被覆写时:(字符串形式为自己覆写的toString决定)
1 | User [id=null, username=null, password=null] |
可以一提的是:
1 | System.out.println(user); |
1 | System.out.println(user.toString()); |
这个两个书写方式都是一样的,打印一个对象时,默认就是调用该对象的toString()方法。
9、notify()
1 | public final native void notify(); |
唤醒正在等待对象监视器的单个线程。
10、notifyAll()
1 | public final native void notifyAll(); |
唤醒正在等待对象监视器的所有线程。
11、wait()
1 | public final void wait() throws InterruptedException { |
导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
12、wait(long timeout)
1 | public final native void wait(long timeout) throws InterruptedException; |
导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。
13、wait(long timeout, int nanos)
1 | public final void wait(long timeout, int nanos) throws InterruptedException { |
导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或一定量的实时时间。
四、结束
写到这里,Object类的JDK源码也就讲完了。
其实我们可以发现,即便是JDK源码,其实也和我们自己写的代码没有太大的区别,我们完全可以自己阅读JDK源码,理解JDK中各个Java类的具体实现,知道JDK源码中的具体运行原理,这对我们学习Java有很大的帮助。
JDK源码中,有很多的类都是JDK自己封装的Java类,这些类都是由最基础的Java知识衍生而来,万变不离其宗!