大家都知道 Object 类是 Java 中所有类的基类,本文将为大家介绍下这位所有类的爹中有哪些公共方法和这些方法的作用。
Object 类位于 java.lang 包中,包含下面这些公共方法:
1、getClass()
源码:
public final native Class<?> getClass();
返回对象运行时的类对象。在类加载的过程中会将.class文件中加载到内存中并生成一个对象,这个对象就是运行时类对象,通过这个方法就可以获取这个类对象。
这个方法是被 final和native修饰的,所以这是一个不可被继承的本地方法。
Object obj = new Object(); Class<?> objClass = obj.getClass();
2、hashCode()
源码:
public native int hashCode();
首先,这个方法也被native修饰,所以默认也是由本地方式实现。默认的返回值是将内存地址转换的hash值,这个值类型是int,所以它的范围也就是 ( -2^31 ~ 2^31-1 )。
通常,我们不用重写或者使用这个方法,但是如果要把对象作为HashMap的键或者是HashSet的值的时候,就要重写它了。
就HashMap来说,会使用这个方法来找到对象在HashMap内部数据中的位置,这个时候如果有两个对象所有属性都相同,我们通常认为这两个对象应该相等了,但是由于这两个对象的内存地址不同,默认hashCode()返回的值就不同,导致HashMap会得出这两个对象不相等的结论,这个结论是不符合我们设想的。
所以这个时候我们要重写hashCode()方法(通常和equals()方法一起重写)达到以下的目的:
-
如果两个对象
equals方法是相等的,那么在这两个对象上调用hashCode方法必须产生相同的结果 -
如果两个对象上调用
hashCode方法产生相同的结果,两个对象equals方法可以不相同
3、equals()
源码:
public boolean equals(Object obj) { return (this == obj); }
比较两个对象是否相等。从源码中可以看出,默认的方法只是比较两个对象的地址是否相同,这种判断基本是达不到我们想要的目的的,通常我们需要的是两个对象的属性全部相等,这两个对象就应该相等,而不非得是在内存中指向同一位置。所以在我们需要进行对象比较的时候,应当重写这个方法,达到我们所需要的目的。
以我们常用的String为例,我们看下它对equals方法的重写
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
除了比较两个对象的地址之外,还比较了字符数组中的对应位置的每一个字符是否相同,如果都相同就可以认为两个字符串相等了。
4、toString()
源码:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
返回类的字符串表示。从源码中就可以看出,这个方法返回了类的全限定性类名和十六进制的hashCode值。通常我们真的需要类的字符串表示的时候返回这点信息肯定是不够的,最好重写这个方法。
System.out.println(new Object().toString()); // 结果:java.lang.Object@72ea2f77
5、wait()、wait(long)、wait(long, int)
源码:
public final native void wait(long timeout) throws InterruptedException; public final void wait() throws InterruptedException { wait(0); } 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); }
这三个方法都是用作线程间通信的,效果是阻塞当前线程,直到有其他线程将其唤醒。需要注意的是这些需要在同步代码块中使用(也就是要先拿到该对象的锁,阻塞后会释放锁),否则会抛出IllegalMonitorStateException异常
Object obj = new Object();
synchronized (obj) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
6、notify()、notifyAll()
源码:
// 随机唤醒之前在当前对象上调用wait方法的一个线程
public final native void notify();
// 唤醒所有之前在当前对象上调用wait方法的线程
public final native void notifyAll();
唤醒之前在当前对象上调用wait方法阻塞的线程。同样的,该方法也需要在同步代码块中执行。
final Object obj = new Object();
new Thread(() -> {
synchronized (obj) {
try {
// 线程休眠5秒
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 唤醒
obj.notify();
}
}).start();
synchronized (obj) {
try {
// 阻塞等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
7、其他
除了上述的公共方法以外,还有几个非公共方法
-
registerNatives()
作用之一是在类加载时主动将本地方法链接到类上,方便类需要调用本地方法时直接使用,以免使用的时候才通过虚拟机定位并链接。
-
clone()
创建并返回对象的副本
-
finalize()
垃圾回收的时候由垃圾回收器调用(永远不要主动调它)