JDK源码阅读01---------Object

1.Object类简述

Object类是Java中所有类的基类,位于java.lang包中。这里写的代码是JDK8中的,其他版本的JDK可能略有不同。
包含的方法如下图:
在这里插入图片描述

2.源码解读


package java.lang;

/**
  *类Object是类层次结构的根类。每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的所有方法。
  */
public class Object {

   /**
     * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用,其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。
     * 函数的执行是在静态代码块中执行的,在类首次进行加载的时候执行。
     */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    /**
     *使用final修饰,说明此方法不能被重写,此方法返回此Object运行时的类型.
     */
    public final native Class<?> getClass();

    /**
     * 返回对象的哈希码,是一个整数。这个方法遵守以下三个规则:
     * 1. 在java程序运行期间,若用于equals方法的信息或者数据没有修改,name同一个对象多次调用此方法,返回的哈希码是相同的。而在两次独立的运行java程序时,对于同一对象,不需要返回的哈希码相同 
     * 2. 如果根据equals方法,两个对象相同,则这两个对象的哈希码一定相同 
     * 3. 假如两个对象通过equals方法比较不相同,那么这两个对象调用hashCode也不是要一定不同,相同也是可以的。但是使用者应该知道对不同的对象产生不同的hashCode是可以提高hash tables的性能的。
     * 
     * 在实际使用中,要尽量保证对于不同的对象产生不同的哈希码。hashCode的典型实现是将对象的内部地址转为一个整数,但是这种实现技术不是Java语言必须要采用的。
     */
    public native int hashCode();

    /**
     * equals方法主要是比较两个对象是否相同,Object中的equals方法比较的是对象的地址是否相同,与 == 无区别。
     * 
     * 在我们实际的编程过程中,如果要是将一个类作为hashMap等类型的键值时,则此类是需要实现equals和hashCode方法,主要是用来比较键值是否相等以及进行哈希化。
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }

    /**
     * 本地clone方法,用于对象的复制,关联:深拷贝与浅拷贝
     */
    protected native Object clone() throws CloneNotSupportedException;

    /**
     * 返回该对象的字符串表示,非常重要的方法
     * getClass().getName();获取字节码文件的对应全路径名例如java.lang.Object
     * Integer.toHexString(hashCode());将哈希值转成16进制数格式的字符串。
     */
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    /**
     * 不能被重写,用于唤醒一个在因等待该对象(调用了wait方法)被处于等待状态(waiting 或 time_wait)的线程,该方法只能同步方法或同步块中调用
     */
    public final native void notify();

    /**
     * 不能被重写,用于唤醒所有在因等待该对象(调用wait方法)被处于等待状态(waiting或time_waiting)的线程,该方法只能同步方法或同步块中调用
     */
    public final native void notifyAll();

    /**
     * 不能被重写,用于在线程调用中,导致当前线程进入等待状态(time_waiting),timeout单位为毫秒,该方法只能同步方法或同步块中调用,超过设置时间后线程重新进入可运行状态
     */
    public final native void wait(long timeout) throws InterruptedException;

    /**
     * wait(long timeout, int nanos)方法的实现中只要nanos大于0,那么timeout时间就加上一毫秒,主要是更精确的控制时间,其他的跟wait(long timeout)一样
     */
    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(0)调用一样。
     * 当前线程必须拥有此对象监视器。
     * 该线程发布对此监视器的所有权并等待,直到其他线程通过调用notify方法或notifyAll方法通知在此对象的监视器上等待的线程醒来,然后该线程将等到重新获得对监视器的所有权后才能继续执行。
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }

    /**
     * 这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法。
     */
    protected void finalize() throws Throwable { }
}

3.相关问题

  1. Object类中有哪些方法使用native关键字修饰的?
	private static native void registerNatives();
    public final native Class<?> getClass();
    public native int hashCode();
    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;
  1. Object类中哪些方法是可以重写的?
	public native int hashCode();
	public boolean equals(Object obj);
	protected native Object clone() throws CloneNotSupportedException;
	public String toString() ;
	protected void finalize() throws Throwable { };
  1. Object类中常见的方法,为什么wait notify会放在Object里边?
1.这些方法存在于同步中;
2.使用这些方法必须标识同步所属的锁;
3.锁可以是任意对象,所以任意对象调用方法一定定义在Object类中。

Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),
使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。
  1. Object中hashCode和equals方法的区别与联系
Object中,equals()方法比较的是对象的内存地址;hashCode()方法是计算对象的散列码(hashCode),并不能表现其唯一性。
当对象的两个对象的equals()方法返回值为true时,两个对象的hashCode值是一定相等的;
但是当两个对象的hashCode值相等时,两个对象equals()的返回值却不一定为true。
  1. 若hashcode方法永远返回1或者一个常量会产生什么结果?
hashCode()方法获取对象的散列值,并不能表现其唯一性,但是有离散性,其意义在于类似于进行hashMap等操作时,加快对象
比较的速度,进而加快对象搜索的速度。

所以,当hashCode()返回常量时,所有对象都出现hash冲突,而hashCode()本身的性能也会降级。

做hash的key的时候效率会极度变低;变量比较也会变慢.
  1. 判断对象相等时,什么情况下只需要重写 equals(),什么情况下需要重写 equals(),hashcode()?
hashCode 顾名思义是一个“散列值码”,散列值,并不能表现其唯一性,但是有离散性,其意义在于类似于进行hashMap等操作时,
加快对象比较的速度,进而加快对象搜索的速度。

可以不重写hashCode()的情况:比如你的对象想放到Set集合或者是想作为Map的key时,那么你必须重写equals()方法,这样才能保证唯一性。
当然,在这种情况下,你不想重写hashCode()方法,也没有错。
但是,对于良好的编程风格而言,你应该在重写equals()方法的同时,也重写hashCode()方法。

必须重写equals()和hashCode():如果你的对象想放进散列存储的集合中(比如:HashSet,LinkedHashSet)
或者想作为散列Map(例如:HashMap,LinkedHashMap等等)的Key时,在重写equals()方法的同时,必须重写hashCode()方法。

hashCode()方法存在的主要目的就是提高效率,但是如果你想把对象放到散列存储结构的集合中时,是必须要重写的。
  1. equals与==的区别
Object中的源码:
	public boolean equals(Object obj) {
   		 return (this == obj);
 	 }
在Object类中equals与==没有区别,都是比较的对象在内存中的地址,所以对于没有重写equals方法的类来说,这两个没有区别;
而在String,Integer等对equals方法进行了重写的类来说,equals与==就存在区别了,
equals方法主要比较的是两个对象内容是否相同,而==比较的是两个对象的内存地址是否相同。

另外,"=="比"equals"运行速度快,因为"=="只是比较引用。
  1. toString()方法什么情况下需要重写
object类里的toString只是把字符串的直接打印,数字的要转化成字符再打印,而对象,则直接打印该对象的hash码。
所以当你要想按照你想要的格式,去字符串化一些对象的时候,就需要重写toString了。
  1. Object的hashcode()是怎么计算的?String中的hashCode()又是怎么计算的?
Object中hashCode()源码:public native int hashCode();
调用c++写的本地方法计算hashCode;

String类重写了hashCode()方法,具体实现的源码:
public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
}
String中的hashCode 的实现其实就是使用数学公式:s[0] * 31^(n-1) + s[1] * 31^(n-2) + ... + s[n-1]
将上一次的计算结果作为31的权重去计算当前的算子,之所以选择31作为系数,主要是出于效率方面的考虑。

在存储数据计算hash地址的时候,我们希望尽量减少有同样的hash地址,所谓“冲突”。
如果使用相同hash地址的数据过多,那么这些数据所组成的hash链就更长,从而降低了查询效率!
所以在选择系数的时候要选择尽量长(31 = 11111[2])的系数并且让乘法尽量不要溢出(如果选择大于11111的数,很容易溢出)的系数,
因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。
  1. 浅拷贝和深拷贝的区别
参见吴渣渣的博客:浅拷贝和深拷贝(谈谈java中的clone)

浅拷贝和深拷贝(谈谈java中的clone)传送门:https://blog.csdn.net/u014727260/article/details/55003402#commentBox

猜你喜欢

转载自blog.csdn.net/AGambler/article/details/83513524