Java程序是由类所组成的。而Object类是所有类的基类,Object中的方法也应当直接存在于派生类中或者由派生类所重写。但是无论如何,都应当了解基类中的方法能够做什么事情,怎么做,因为Object中的方法五一是java学习中最为基础的知识。
registerNatives?
一上来我就被class Object代码块中的这样两行代码整蒙了:
private static native void registerNatives();
static {
registerNatives();
}
首先定义了一个registerNatives方法,与通常的java方法不同,我并没能用Go to Declaration找到这个方法的源码,然后由看了一下前面的关键字,native?这是个啥?
搜了一下,发现native修饰的方法是定义在本地代码中的,另有其他语言来实现,主要是用来实现对操作系统底层的访问和操作。具体实现上,是包括在JDK的Java Native Interface(JNI)包当中的。可以把java语言和其他语言嵌套在一起。
所以这个registerNatives做的是与本地的交互工作,引用StackOverflow上的说法,这个方法具体完成的工作就是在java中给本地方法一个映射,使得java在运行时可以直接调用本地方法,而这个类方法时static的,也就是在Object类加载时最先执行的,Object方法又必然在所有类之前加载。因此,这个方法一定会在其他方法之前运行,保证其他方法在运行时可以准确的找到本地方法。
构造方法
@HotSpotIntrinsicCandidate
public Object() {}
就是建立一个简单的Object对象,初始化函数本身很平实,陌生的敌方就是 @HotSpotIntrinsicCandidate这个注释,表示的是JVM中对这一方法有一套单独的实现方法。
其他本地方法
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
@HotSpotIntrinsicCandidate
public native int hashCode();
getClass主要是用于反射,通过给定具体的对象,获取其所从属的类对应的Class类。
hashCode是返回对象的哈希值,用于今后要说的HashTable和HashSet等。
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
又是一个native方法,还有一点需要注意,就是java中的clone是进行深复制,即复制所有的引用子对象。
equals
public boolean equals(Object obj) {
return (this == obj);
}
在各种笔试面试题中经常出现的万恶之源。这里看一下,原方法是必须得是引用相同的对象才可以!这点切记。但是为啥到了String字符串比较就不一样了呐?来看一下String类中的代码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
是因为String类重写了equals方法,在地址不相等时,通过比较字符串的值来补救。
toString
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
toString方法就是将一个Object对象用String的方法打出来,返回类名称+@+这个对象的哈希码。
线程相关
wait与notify、notifyall是java中常用的两个线程间通信方法,具体的使用可以详见如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例。这里主要说一下参数的作用。
先来看一下wait函数及其重载。
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void wait() throws InterruptedException {
wait(0L);
}
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
timeoutMillis++;
}
wait(timeoutMillis);
先来看一下第一个wait,只有一个long参数,就是让当前的线程放弃此对象的锁并且监视该对象是否有notify或者notifyall的动作,并且最多等待一个timeoutMillis的时长,这段时间内如果有其他线程进行了notify与notifyall,那么就继续,如果超时,继续运行。第二个是无参的wait就是参数为0的情况,无论过多久,该进程都要去等待该对象被唤醒;第三个就是添加了一个毫秒,将毫秒转化为微秒级别。
接下来看一下notify和notifyall
@HotSpotIntrinsicCandidate
public final native void notify();
@HotSpotIntrinsicCandidate
public final native void notifyAll();
这两个也是一对孪生兄弟,两者的作用也相差无几。notify是一个小气鬼,每次只会通知一个在wait该对象的线程,但是究竟选择哪个线程呐?答案是随机,因此notify本身会造成一些不确定性。二notifyAll则好多了,一次性的唤醒所有在等待该对象的线程。
finalize
@Deprecated(since="9")
protected void finalize() throws Throwable { }
在该对象被回收时所执行的方法,与C++中的析构函数不同,这一语句只会被执行一次,之后即便是该对象没有真正的被GC所回收,也不会再次运行。