Java源码分析—Object

本文的分析基于JDK 1.8

1.static native void registerNatives()

private static native void registerNatives();
static {
registerNatives();
}

该方法只是对几个本地方法进行注册(即初始化时将java方法映射到C的方法)。需要注意的是,很多类中都有这个方法,但是执行注册的目标是不同的。System类中也有该方法,但是它注册的方法是另一些方法。

2. native Class<?> getClass()

这也是一个本地方法,返回一个对象的运行时类。返回运行时的实际类型

3. native int hashCode()

这也是一个本地方法,返回值是对象的一个哈希值。
在编写实现hashCode()方法时,我们需要遵守一些约定:
在应用程序执行期间,如果一个对象用于equals()方法的属性没有被修改的话,那么要保证对该对象多次返回的hashcode值要相等。
如果2个对象通过equals()方法判断的结果为true,那么要保证二者的hashcode值相等。
如果2个对象通过equals()方法判断的结果为false,那么对二者hashcode值是否相等并没有明确要求。如果不相等,那么能够提升散列表的性能。

4. boolean equals(Object obj)

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

从这里我们可以看到,equals(obj)方法最根本的实现就是‘==’,因此对于一些自定义类,如果没有重写hashcode()方法和equals()方法的话,利用‘==’和equals()方法比较的结果是一样的。对于‘==’比较的是地址,equals()方法比较的是内容这种说法,是片面的。(虽然在最常用的String类中是这样的)。
对equals重新需要注意五点:
1. 自反性:对任意引用值X,x.equals(x)的返回值一定为true;
2. 对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
3. 传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true ;
4. 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变;
5. 非空性:任何非空的引用值X,x.equals(null)的返回值一定为false 。

5. native Object clone()

protected native Object clone() throws CloneNotSupportedException;

这个也是本地方法。需要注意的是该方法是“浅拷贝”的。

浅拷贝:如果一个对象内部还有一个引用类型的基本变量,那么再拷贝该对象的时候,只是在通过clone方法新产生的新对象中拷贝一个该基本类型的引用。换句话说,也就是新对象和原对象他们内部都含有一个指向同一对象的引用。

深拷贝:拷贝对象的时候,如果对象内部含有一个引用类型类型的变量,那么就会再将该引用类型的变量指向的对象复制一份,然后引用该新对象。

下面2个图可以帮助理解:

深拷贝与浅拷贝

Java 使用GeoTools解析dxf javacode分析_引用类型

6. String toString()

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

此方法返回对象的字符串描述,子类重写此方法

7.void notify()notifyAll()方法

public final native void notify();
public final native void notifyAll();

这2个方法都是本地方法,前者唤醒一个在当前对象监视器上等待的线程。后者唤醒所有在当前对象监视器上等待的线程。
我们应该注意的是,这2个方法应当仅仅被拥有对象监视器的线程所调用。而一个线程成为对象的监视器的拥有者有三种方法:
- 执行该对象上的一个同步(synchronized)方法
- 执行一个同步在在对象上的代码块
- 执行该对象的类上的静态同步方法

8.void wait(long timeout); void wait(long timeout,int nanos); void wait();

本地方法。 
首先,调用该方法会抛出中断异常(InterruptedException),调用时应该用try catch捕捉。
 - public final native void wait(long timeout) throws InterruptedException;
本地方法 
该方法也会抛出中断异常,调用时应捕捉。
该方法使当前线程等待,直到另外一个线程调用该对象的notify或notifyAll方法,或者等待时间已到,当前线程才会从等待池移到运行池。 
如果在wait之前或者wait的时候,当前线程被中断了,那么直到该线程被恢复的时候才会抛出中断异常(InterruptedException)。
 - public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

    <span >if</span> (nanos < <span >0</span> || nanos > <span >999999</span>) {
        <span >throw</span> <span >new</span> IllegalArgumentException(
                            <span >"nanosecond timeout value out of range"</span>);
    }

    <span >if</span> (nanos > <span >0</span>) {
        timeout++;
    }

    wait(timeout);
}


该方法可能会抛出中断异常,调用时应捕捉。源码只是将timeout四舍五入,并没有提供更高精度的控制。

<span >public</span> <span >final</span> <span >void</span> <span >wait</span>() <span >throws</span> InterruptedException {
 wait(<span >0</span>);
  • }

9.void finalize();

- protected void finalize() throws Throwable { }
 -

垃圾回收器在认为该对象是垃圾对象的时候会调用该方法。子类可以通过重写该方法来达到资源释放的目的。
在方法调用过程中出现的异常会被忽略且方法调用会被终止。
任何对象的该方法只会被调用一次。

总结

本地方法有:除了equal()、wait()、wait(long timeout,long nanos)和toString()方法其余均是本地方法。
equals()方法经常被重写,而在重写的时候,hashcode()方法应当也重写。例如:当用equals()方法判断对象内容一致的时候,如果hashcode()方法没有重写而导致使用该方法得到的哈希值不同,这样就会产生歧义(比如在Set集合中是通过对象的hashcode值来“辨别”对象的,如果对象内容一致,hashcode值却不一致,会在使用Set集合操作时产生歧义)。(其实我感觉这应该就是制定编写hashcode()方法需要遵守那几个规范的原因)



Java源码分析—Object

本文的分析基于JDK 1.8

1.static native void registerNatives()

private static native void registerNatives();
static {
registerNatives();
}

该方法只是对几个本地方法进行注册(即初始化时将java方法映射到C的方法)。需要注意的是,很多类中都有这个方法,但是执行注册的目标是不同的。System类中也有该方法,但是它注册的方法是另一些方法。

2. native Class<?> getClass()

这也是一个本地方法,返回一个对象的运行时类。返回运行时的实际类型

3. native int hashCode()

这也是一个本地方法,返回值是对象的一个哈希值。
在编写实现hashCode()方法时,我们需要遵守一些约定:
在应用程序执行期间,如果一个对象用于equals()方法的属性没有被修改的话,那么要保证对该对象多次返回的hashcode值要相等。
如果2个对象通过equals()方法判断的结果为true,那么要保证二者的hashcode值相等。
如果2个对象通过equals()方法判断的结果为false,那么对二者hashcode值是否相等并没有明确要求。如果不相等,那么能够提升散列表的性能。

4. boolean equals(Object obj)

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

从这里我们可以看到,equals(obj)方法最根本的实现就是‘==’,因此对于一些自定义类,如果没有重写hashcode()方法和equals()方法的话,利用‘==’和equals()方法比较的结果是一样的。对于‘==’比较的是地址,equals()方法比较的是内容这种说法,是片面的。(虽然在最常用的String类中是这样的)。
对equals重新需要注意五点:
1. 自反性:对任意引用值X,x.equals(x)的返回值一定为true;
2. 对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
3. 传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true ;
4. 一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变;
5. 非空性:任何非空的引用值X,x.equals(null)的返回值一定为false 。

5. native Object clone()

protected native Object clone() throws CloneNotSupportedException;

这个也是本地方法。需要注意的是该方法是“浅拷贝”的。

浅拷贝:如果一个对象内部还有一个引用类型的基本变量,那么再拷贝该对象的时候,只是在通过clone方法新产生的新对象中拷贝一个该基本类型的引用。换句话说,也就是新对象和原对象他们内部都含有一个指向同一对象的引用。

深拷贝:拷贝对象的时候,如果对象内部含有一个引用类型类型的变量,那么就会再将该引用类型的变量指向的对象复制一份,然后引用该新对象。

下面2个图可以帮助理解:

深拷贝与浅拷贝

Java 使用GeoTools解析dxf javacode分析_引用类型

6. String toString()

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

此方法返回对象的字符串描述,子类重写此方法

7.void notify()notifyAll()方法

public final native void notify();
public final native void notifyAll();

这2个方法都是本地方法,前者唤醒一个在当前对象监视器上等待的线程。后者唤醒所有在当前对象监视器上等待的线程。
我们应该注意的是,这2个方法应当仅仅被拥有对象监视器的线程所调用。而一个线程成为对象的监视器的拥有者有三种方法:
- 执行该对象上的一个同步(synchronized)方法
- 执行一个同步在在对象上的代码块
- 执行该对象的类上的静态同步方法

8.void wait(long timeout); void wait(long timeout,int nanos); void wait();

本地方法。 
首先,调用该方法会抛出中断异常(InterruptedException),调用时应该用try catch捕捉。
 - public final native void wait(long timeout) throws InterruptedException;
本地方法 
该方法也会抛出中断异常,调用时应捕捉。
该方法使当前线程等待,直到另外一个线程调用该对象的notify或notifyAll方法,或者等待时间已到,当前线程才会从等待池移到运行池。 
如果在wait之前或者wait的时候,当前线程被中断了,那么直到该线程被恢复的时候才会抛出中断异常(InterruptedException)。
 - public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

    <span >if</span> (nanos < <span >0</span> || nanos > <span >999999</span>) {
        <span >throw</span> <span >new</span> IllegalArgumentException(
                            <span >"nanosecond timeout value out of range"</span>);
    }

    <span >if</span> (nanos > <span >0</span>) {
        timeout++;
    }

    wait(timeout);
}