Java源码-Class Object (java.lang)

image.png

Object类是Java所有类的基类,所有类都默认继承Object。

1、registerNatives()

//本地方法,用c(c++)实现在dll文件内,通过JNI调用
private static native void registerNatives();
//对象初始化时调用此方法
static {
    registerNatives();
}
复制代码

2、getClass()

//本地方法,返回该类的Class对象
public final native Class<?> getClass();
复制代码

3、hashCode()

//本地方法,返回对象的哈希码值
public native int hashCode();
复制代码

4、equals(Object obj)

//比较两对象是否相等
public boolean equals(Object obj) {
    return (this == obj);
}
复制代码
  • equals()方法在Object类中实现为 “==”, “==”用来比较两个对象的地址是否相等,即当两个对象为同一对象时才会返回true。类使用该方法时通常有以下两类:

    • 若某个子类没有覆盖该方法,当通过equals()比较两个对象是否相等时,实际上比较的是两个对象是否为同一对象;
    • 若某个子类重写了该方法,如String类将equals()方法重写为比较两个类的内容是否相等,此时比较的就是两个类的内容,内容相等(即使不为同一个类)返回true。
  • 哈希码的作用是确定对象在哈希表的索引位置,Object类中的hashCode()方法是本地方法,用c(c++)编写,默认通常将对象在堆上的内存地址转换成整数再返回,因此,如果未重写hashCode()方法,两个对象的哈希值不会相等(地址不同)。hashCode()与equals()的常规协定是:

    • 在Java程序执行期间,同一个对象多次调用hashCode(),必须返回相同的值;
    • equals()判断两个对象相等时,两个对象的hashCode()返回值也必须相等;
    • equals()判断两个对象不相等时,两个对象的hashCode()返回值不一定相等。

    因此,比较两个对象时,可以先比较hashCode是否相等,若不相等,说明两个对象不相等;若hashCode相等,此时再调用equals()比较。这样就能大大减少equals的次数,提高执行速度。

基于上述hashCode()与equals()的常规协定,重写equals()方法时必须也要重写hashCode()方法

5、clone()

//创建并返回此对象的副本
//protected修饰:如果一个类未显示地重写该方法,其他类就不能直接调用该类实例的clone()方法
protected native Object clone() throws CloneNotSupportedException;
复制代码

clone()方法规定:

  • 克隆的对象必须要实现 Cloneable 接口并重写 clone 方法,否则会报 CloneNotSupportedException 异常;

  • clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出CloneNotSupportedException;

  • 该方法会创建类的新实例,并将其所有字段初始化为原对象相应字段的内容(字段通过赋值,字段本身不被克隆)-- 浅拷贝


补充:来看一下Cloneable接口(java.lang)源码

image.png

查看源码发现Cloneable接口中什么都没有定义,因此接口Cloneable只是一个标识,标识实现这个接口的类能够被克隆。(注意,并不是类实现了Cloneable接口才具有被克隆的性质,clone()是每个类都具有的方法而不是Cloneable接口的方法,只不过在clone()方法里要求被克隆的对象必须实现Cloneable接口并重写了clone()方法,否则会报CloneNotSupportedException异常)


6、toString()

//返回对象的字符串表示形式
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
复制代码

7、notify()、notifyAll()

//唤醒在阻塞队列正在等待本对象的某个线程,被唤醒的线程进入就绪队列
public final native void notify();
复制代码
//唤醒在阻塞队列正在等待本对象的所有线程,被唤醒的线程进入就绪队列
public final native void notifyAll();
复制代码

只有持有该对象监视器的线程才能执行该方法,如果当前线程不是此对象的监视器持有者,报异常IllegalMonitorStateException

8、wait()

//使当前线程等待,放弃此对象监视器,进入阻塞队列,等到其他持有此对象监视器的线程执行notity()或notifyAll(),或指定的timeout(毫秒)已到,该线程才会被唤醒,进入就绪队列
public final native void wait(long timeout) throws InterruptedException;

复制代码
//timeout=0,表示只能等其他线程行notity()或notifyAll(),才能被唤醒
public final void wait() throws InterruptedException {
    wait(0);
}
复制代码
//比wait(long timeout)更精细的时间   1000000*timeout+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()和wait()在同步方法或同步代码块中成对使用

9、finalize()

protected void finalize() throws Throwable { }
复制代码
  • 当垃圾收集器通过可达性分析确定不再有对该对象的引用时,会对它进行第一次标记,随后进行一次筛选,筛选条件是这个对象是否覆盖了finalize()方法或者finalize()方法是否已经被虚拟机调用过,如果是,对象会被第二次标记并被回收。
  • 如果不是,那么该对象会被放进F-Queue队列中,并在稍后由一条虚拟机自动建立的、低调度低优先级(不一定马上执行)的Finalizer线程去执行它们的finalize()方法,然后垃圾收集器会对F-Queue队列中的对象进行第二次标记,如果finalize()中对象重新与引用链建立了关联关系,那么该对象就不会被第二次标记,从而逃掉被垃圾收集器回收的命运。但是该对象面临下一次回收的时候,就不会再触发finalize()而被直接回收,因为finalize()只能被执行一次。
  • finalize()方法运行代价高,不确定性大,无法保证各个对象的调用顺序,如今已被官方声明不推荐使用。如果要做一些"关闭外部资源"之类的清理工作,可以使用try-finally而不要使用finalize()。(完全可以忘记finalize()方法)

猜你喜欢

转载自juejin.im/post/7077875382300442661
今日推荐