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个图可以帮助理解:
深拷贝与浅拷贝
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个图可以帮助理解:
深拷贝与浅拷贝
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);
}