JDK源码-Object类解析

298 阅读7分钟

一、JDK源码

作为Java中所有Class的爸爸,Object类无疑是Java中非常重要的一个类,今天我们就直接来阅读以下JDK源码中的Object类,对其中的代码进行解析。 首先,废话不多说,我们直接上源码:

package java.lang;

public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }
    
    public final native Class<?> getClass();
    
    public native int hashCode();
    
    public boolean equals(Object obj) {
        return (this == obj);
    }
    
    protected native Object clone() throws CloneNotSupportedException;
    
    public String toString() {
        return getClass().getName() + "@" +Integer.toHexString(hashCode());
    }
    
    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);
    }
    
    protected void finalize() throws Throwable { }
    
}

二、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类型的对象。 例如:

Object object = new Object();

2、registerNatives()

private static native void registerNatives();

这个方法是private的,是私有的,所以这个方法是Object类独有的方法,不会被其子类继承。 registerNatives() 方法独特的地方在于,使用了 native关键字进行修饰,在java中,使用native关键字进行修饰,那就说明这个方法的底层实现,并不是由Java代码来进行编写的,而是调用了其他语言编写的接口来进行实现的。 我们可以从源码中发现:

static {
    registerNatives();
}

在Object类中有一个静态代码块,直接调用了这个方法,也就是说,这个方法是所有的类进行实例化的时候都会执行的方法。 这个方法和Java语言的底层实现有关系,我们只需要了解即可,并不是今天的重点。

3、clone()

protected native Object clone() throws CloneNotSupportedException;

clone()方法是一个受保护的方法,作用是克隆一个对象,我们可以实例化一个Object类的对象,看看Object对象可以直接调用哪些方法:

在这里插入图片描述

从上面的图我们可以发现,其实Object类的实例对象竟然也无法调用clone()方法,那么Java中该怎么调用clone()方法呢? 方法很简单,实现Cloneable接口,并覆写clone()方法即可! 我们先来写一个User类,并让User类实现Cloneable接口,并覆写clone()方法。

public class User implements Cloneable{
	private Integer id;
	private String username;
	private String password;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

我们写好了User类,并让User类实现了Cloneable接口和覆写clone()方法,那么我们来实例化一个User对象。

在这里插入图片描述

从上面的图我们可以看见,除去User类自己的6个setter和getter方法之后,User类可以调用的方法有10个,其中就包括之前无法调用的clone()方法。 说完了实现,我们在说说clone方法的结果。

User user1 = new User();
User user2 = (User) user1.clone();
System.out.println(user1 == user2);

执行上面的代码,得到结果:

false

从这个结果我们可以看出,其中clone()方法克隆出来的对象,和原来的对象并不是同一个,而是从新创建了一个新的对象。

4、finalize()

protected void finalize() throws Throwable { }

finalize()方法是一个和Java的垃圾回收机制有关的方法。 这个方法会在Java的垃圾回收机制回收对象所占内存之前被调用,即当一个对象被Java虚拟机宣告死亡时会先调用它finalize()方法,让这个对象处理它被回收前的最后事情。 如果我们在Java类中覆写了该方法,可以通过覆写的方法,摆脱本身被Java垃圾回收机制回收的命运。 对于这个方法,我们只限于了解,并不深究。

5、getClass()

public final native Class<?> getClass();

接下来要说的是9个公有方法,也是Java类中最为常用的9个方法。第一个就是getClass()。 通过这个方法,可以获取Java对象的字节码对象,具体操作如下:

User user = new User();
Class clazz = user.getClass();

6、hashCode()

public native int hashCode();

hashCode()方法,可以获取Java对象的哈希码值,具体操作如下:

User user = new User();
int code = user.hashCode();
System.out.println(code);

运行结果如下:

366712642

7、equals(Object obj)

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

equals(Object obj)方法,其实从源码不难发现,equals方法最底层还是调用了==运算符来实现的,也就是说Object类的equals方法和双等号运算符并没有什么区别。

也就是基本数据类型比较值是否相等,应用数据类型比较内存地址是否相等

而String类型的equals方法之所以可以比较应用数据类的值是否相等,那是因为String类中重写了equals方法。

User user1 = new User();
User user2 = user1;
System.out.println(user1 == user2);

结果如下:

true

8、toString()

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

toString()方法,在JavaBean中经常被覆写的方法,用于返回对象的字符串表示形式。

User user = new User();
System.out.println(user.toString());

没有被覆写时结果为:(包名+引用内存地址)

com.day01.object.User@15db9742

当toString()方法被覆写时:(字符串形式为自己覆写的toString决定)

User [id=null, username=null, password=null]

可以一提的是:

System.out.println(user);
System.out.println(user.toString());

这个两个书写方式都是一样的,打印一个对象时,默认就是调用该对象的toString()方法。

9、notify()

public final native void notify();

唤醒正在等待对象监视器的单个线程。

10、notifyAll()

public final native void notifyAll();

唤醒正在等待对象监视器的所有线程。

11、wait()

public final void wait() throws InterruptedException {
    wait(0);
}

导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。

12、wait(long timeout)

public final native void wait(long timeout) throws InterruptedException;

导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。

13、wait(long timeout, int nanos)

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);
}

导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或一定量的实时时间。

四、结束

写到这里,Object类的JDK源码也就讲完了。 其实我们可以发现,即便是JDK源码,其实也和我们自己写的代码没有太大的区别,我们完全可以自己阅读JDK源码,理解JDK中各个Java类的具体实现,知道JDK源码中的具体运行原理,这对我们学习Java有很大的帮助。 JDK源码中,有很多的类都是JDK自己封装的Java类,这些类都是由最基础的Java知识衍生而来,万变不离其宗!