java.lang.Object是Java中所有类的父类(super class),是唯一一个没有父类的Java类。所有对象都会自动继承Object,通过Object可以了解一个对象最基本的行为。由于Object涉及到JVM加载的过程,很多方法都是native方法,可以通过下载OpenJDK源码来查看方法的具体内容。
Method | Return | Paremeters |
---|---|---|
registerNatives | - | - |
getClass | Class | - |
hashCode | boolean | - |
equals | int | Object obj |
clone | Object | - |
toString | String | - |
notify | - | - |
notifyAll | - | - |
wait | - | long timeout, int nanos |
finalize | - | - |
在OpenJDK源码中,可以看到有一份文件1里包含着registerNatives()
的实现:
static JNINativeMethod methods[] = {
{
"hashCode", "()I", (void *)&JVM_IHashCode},
{
"wait", "(J)V", (void *)&JVM_MonitorWait},
{
"notify", "()V", (void *)&JVM_MonitorNotify},
{
"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{
"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
}
/****
* used in RegisterNatives to describe native method name, signature, and function pointer.
*/
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
native方法在JNI中根据类路径添加了前缀。比如:java.lang.Object.registerNatives被命名为Java_java_lang_Object_registerNatives。可以看到除了getClass方法外,其余所有Object类的native方法都经由registerNatives进行注册。registerNatives()
在java.lang.Object中,经由static块进行调用,确保所有方法在一开始就进行加载。
如果需要在JNI中重新绑定native方法或者注册新的方法,同样可以改写JDK底层的native方法的实现。
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}
Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.2
hashCode()
的行为将会影响哈希表(如HashMap)的性能。hashCode()
的设计遵循以下原则:
hashCode()
,返回值要保持一致。equals()
返回相同的对象,hashCode()
必须相同。equals()
返回不同的对象,hashCode()
不要求不相等。但若能保证hashCode()
的不同,会对哈希表有性能提升。显然,是由于哈希碰撞的原因。hashCode()
能够对不同的对象返回不同的哈希值。典型实现是以对象的地址来作为哈希值,但是这并非Java语言所规定的实现。假设x
,y
,z
为非空(non-null)的对象,该函数返回有以下性质3456:
x.equals(x)
应返回true;x.equals(y)
有且在y.equals(x)
返回true时返回true;x.equals(y)
且y.equals(z)
,则x.equals(z)
;x.equals(y)
,返回结果相同;以上的原则其实和代数世界中的等号性质完全一致。注意,由于hashCode()
的原则是通过equals()
来进行限定的,这两个方法在覆写(Override)时常需要一并考量。
clone()
返回该对象的拷贝,通常情况下需要保证返回的clone对象与当前对象引用的地址不同,但equals()
为true。
clone()
方法还遵循许多惯例(convention):
通常看到以下方式实现clone()
:
public Object clone() {
return super.clone();
}
若所有父类都遵循以上实现,最终将会调用Object.clone
,通过RTTI(run-time type identification)返回调用clone()
方法对应的对象的一个实例。Object.clone
会将内存进行bitwise的复制。但是仍然存在一个问题就是对于引用也直接进行拷贝,可以参看以下示例,返回结果表明clone对象内部的nested确实是与原对象相同引用。
public class CloneTest implements Cloneable {
class Nested {
private int val;
public Nested(int val) { this.val = val; }
}
Nested nested = new Nested(1);
@Override
protected Object clone() {
try {
return super.clone();
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
CloneTest ct = new CloneTest();
CloneTest clone = (CloneTest) ct.clone();
System.out.println(ct.nested == clone.nested); // print true
}
}
一个Object.clone
底层的实现会对没有实现Clonable接口的对象直接抛出CloneNotSupportedException,所以想要通过Object.clone
完成克隆,就需要实现Clonable接口。实际上,该接口内部没有任何内容。
提供对象的文字描述。Object中的toString返回值由类名以及哈希值的十六进制表示共同组成。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
notify()
wakes up all threads that are waiting on this object’s monitor. A thread waits on an object’s monitor by calling one of the wait methods.11
Java中所有对象内皆可以作为线程间同步的临界区,每个Object内部都含有一个monitor12。例如,synchronized(obj)
即获取obj
的monitor。而notify()
、notifyAll()
、wait()
都是通过ObjectMonitor进行线程同步的手段。当然此外,java.lang.Thread
也提供了一些join()
,sleep()
等手段来控制线程的状态,在另一篇文章再研究其区别。
这三个函数涉及很多线程同步的内容,待完全弄明白后一一总结。
Java GC将会调用即将回收的对象的fianlize()
,用于完成对象回收前的结束工作。JVM将待执行的finalize()
放入名为F-Queue的队列。GC内部的Finalizer线程会以一个较低优先级执行F-Queue中的函数,所以即便一个不可达(not accessible)的对象在执行GC后也不保证会即刻执行finalize()
13:
finalize()
确保会被调用,但JVM不保证完全执行。finalize()
至多仅会被调用一次。finalize()
抛出的异常将会被忽略,并终止finalization。根据《深入理解Java虚拟机 JVM高级特性与最佳实践》中的描述,这个函数实际提供了C++的析构函数的类似功能,但是try-final块等也能完成相关的资源回收工作,并且效率高得多。试想C++的heap上的对象多数由程序员自己或者借由智能指针来完成析构,并且严格保证与构造顺序相反,并不会经过像JVM中这样一个低优先级的、不可保证顺序、不能保证完成整个函数执行的调用过程。