Object的方法列表如下.
![](https://img.haomeiwen.com/i2400535/0c9a6673fd961a85.png)
registerNatives — 自定义native方法命名
registerNatives完成自定义native方法命名,在静态代码块被调用完成命名的初始化.
对于带有native修饰的方法,JVM需要知道对应的native代码的方法命名.默认情况下,有一套默认规则.例如对于java.lang.Object.registerNatives
,对应的C语言命名为Java_java_lang_Object_registerNatives
.
假设有如下native方法
package com.github.alonwang
public RegisterNativesDemo{
public native void nativeMethod();
}
默认生成的native方法命名为Java_com_github_alonwang_RegisterNativesDemo_nativeMethod
.
而Object中的native方法通过registerNatives达成自定义命名,例如 Object.hashCode对应的native方法命名为JVM_IHashCode
(参见这里).
clone — 生成实例拷贝
如果一个类实现了Cloneable接口(只是一个标记接口),就能调用clone方法生成当前实例的一份拷贝.只能完成浅拷贝.
public class CloneDemo implements Cloneable {
private int a;
private Integer A;
public CloneDemo(int a, Integer a1) {
this.a = a;
A = a1;
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneDemo a = new CloneDemo(1, 1);
CloneDemo b = (CloneDemo) a.clone();
System.out.println("a.a == b.a ? " + (a.a == b.a));
System.out.println("a.A equals b.A ? " + (a.A.equals(b.A)));
System.out.println("a.A == b.A ? " + (a.A == b.A));
}
}
输出:
a.a == b.a ? true
a.A equals b.A ? true
a.A == b.A ? true
可以看到原实例和拷贝实例中的字段A,引用了同一个对象.任何一方修改A都会影响到另一方.
equals &&hashCode— 对象是否相等&哈希表
equals判定两个对象是否相等,需要满足以下条件
- 自反性 x.equals(x) == true
- 对称性 x.equals(y) == y.equals(x)
- 传递性 if x.equals(y)==true,y.equals(z)==true, then x.equals(z)==true
- 一致性 对于x.equals(y),在x,y均未发生变化时,无论调用多少次,结果都是相同的
- 对于任何不是null的x,x.equals(null)==false
生成对象的哈希码,有如下约定:
- 如果两个对象根据equals比较相等,那么这两个对象的哈希码也相等(反之并不成立)
- 如果两个对象根据equals比较并不相等,不要求两个对象的哈希码不同(但是如果不同可以提高哈希表的性能)
对于Object而言,equals直接比较两个对象的内存地址,hashCode则是根据内存地址计算. 这符合Object的定义: 内存地址相同的对象才是相等的.
哈希表对equals和hashCode的要求
如果对象需要作为哈希表的key,就需要特别关注equals和hashCode的关系,先简单介绍下HashMap.get(key)的过程:
-
首先根据key的哈希码确定key在entry数组中的位置entry[i]
-
如果entry[i]为空,说明不存在,如果不为空,需要通过equals判断依次判断key和entry[i]链表中的key是否相等,如果相等,说明存在,否则说明不存在.
为了实现上面的过程,当equals与hashCode结合时,有如下关系:
- 必须同时覆盖equals和hashCode
- equals相等时,hashCode必须相等
- hashCode相等,equals不一定相等
那么何时需要覆盖equals和hashCode呢?
先来推断2,假设equals相等时,hashCode不相等,那么在11这一步,两个根据equals判定相等的key,对应的哈希码时不同的,因此在11中计计算出它们在entry数组中的位置也是不同的,这样就导致"相同"的key在hashMap中对应多个value. 这样显然违背了hashMap的定义,因此equals相等时,hashCode必须相等.
对于3,由22自然可以得出hashCode相等,equals不一定相等.
对于1, 如果未同时覆盖两者,很可能违背2.
总结一下,如果你要将对象作为哈希表中的key,才需要特别注意两者的关系,不然还是保持默认最好.
finalize — 对象的终结
一个对象真正被回收,至少需要经过两次标记
- 对经过可达性分析无法到达的对象进行第一次标记并筛选,如果对象覆盖了finalize方法且finalize方法没有被虚拟机调用过,有必要执行finalize方法,否则没有必要执行finalize方法,直接清理
- 如果对象被判定为有必要执行finalize方法,这个对象会被放置到一个称为F-Query的队列中,稍后由虚拟机创建的、低优先级的Finalizer会去执行(为了安全,会触发这个对象的finalize方法但不承诺会等待它运行结束)它,如果在finalize方法中对象重新与引用链上的任何一个对象建立联系,在第二次标记中就会被移除“即将回收”的集合,否则就真的被回收。
finalize在设计是是为了和C++保持兼容, 一个错误的说法是在finalize中执行资源的回收,事实上这是很不安全的.回收资源使用try,finally更为合适.<深入理解Java虚拟机>中建议忽略这个方法的存在
getClass — 获取Class对象
Class对象是JVM生成用来保存对象的类的信息的.通过Class对象可以获取类的元数据,通过反射获取类的实例,用途极广.
notify,notifyAll,wait — Java的底层同步机制
下面的代码分别演示了
-
synchronizedTest 多个线程竞争内置锁
-
notifyTest 某个线程因资源问题主动放弃内置锁等待被其他线程唤醒
-
notifyAll 多个线程因资源问题主动放弃内置锁等待被其他线程唤醒
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;public class ObjectTest {
private static final Object lockObj = new Object();
private static final int THREAD_COUNT = 5;public static void main(String[] args) { synchronizedTest(); notifyTest(); notifyAllTest(); } private static void synchronizedTest() { System.out.println("start synchronized test..."); //等待所有线程执行结束 CountDownLatch latch = new CountDownLatch(THREAD_COUNT); IntStream.rangeClosed(1, THREAD_COUNT).forEach(i -> { //模拟每个线程执行三次业务操作 new Thread(() -> { for (int k = 0; k < 3; k++) { synchronized (lockObj) { System.out.println(Thread.currentThread().getName() + " execute " + k); try { TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } //睡眠一会,让其他线程有机会获取同步锁. try { TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(10)); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + " execute finished"); latch.countDown(); }, "synchronized-Thread-" + i).start(); }); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synchronized test finish..."); } /** * notify调用后,当前线程会在同步代码块执行完成后,再释放锁.并不是notify调用后直接释放锁. * wait调用后当前线程会保存线程上下文,释放锁,进入等待状态.被notify唤醒后,恢复线程上下文,继续执行. * 下例中wait-Thread运行后在lockObject上等待,而后被notify-Thread唤醒. * 在notifyThread执行notify后,并没有立刻释放锁,而是在同步块执行结束后才释放的锁. * console: * wait-before * notify-before * notify-after * wait-after */ private static void notifyTest() { System.out.println("notify test start..."); CountDownLatch finishLatch=new CountDownLatch(2); //确保wait-Thread在notify-Thread之前进入同步代码块. CountDownLatch waitLatch = new CountDownLatch(1); new Thread(() -> { synchronized (lockObj) { waitLatch.countDown(); System.out.println("wait-before"); try { lockObj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("wait-after"); } finishLatch.countDown(); }, "wait-Thread").start(); new Thread(() -> { try { waitLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockObj) { System.out.println("notify-before"); lockObj.notify(); System.out.println("notify-after"); } finishLatch.countDown(); }, "notify-Thread").start(); try { finishLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("notify test finish..."); } /** * console: * wait-before-wait-Thread-1 * wait-before-wait-Thread-3 * wait-before-wait-Thread-2 * wait-before-wait-Thread-4 * wait-before-wait-Thread-5 * notify-before * notify-after * wait-after-wait-Thread-5 * wait-after-wait-Thread-4 * wait-after-wait-Thread-2 * wait-after-wait-Thread-3 * wait-after-wait-Thread-1 */ private static void notifyAllTest() { System.out.println("notifyAll test start..."); CountDownLatch finishLatch=new CountDownLatch(THREAD_COUNT+1); //确保wait-Thread在notify-Thread之前进入同步代码块. CountDownLatch waitLatch = new CountDownLatch(THREAD_COUNT); IntStream.rangeClosed(1, THREAD_COUNT).forEach(i -> { new Thread(() -> { synchronized (lockObj) { waitLatch.countDown(); System.out.println("wait-before-" + Thread.currentThread().getName()); try { lockObj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("wait-after-" + Thread.currentThread().getName()); } finishLatch.countDown(); }, "wait-Thread-" + i).start(); }); new Thread(() -> { try { waitLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockObj) { System.out.println("notify-before"); lockObj.notifyAll(); System.out.println("notify-after"); } finishLatch.countDown(); }, "notify-Thread").start(); try { finishLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("notifyAll test finish..."); }
}
toString — 生成对象的字符串表示
默认生成 类的名字@实例的哈希码的16进制,有时为了调试方便, 会覆盖toString输出更有用的信息
registerNatives: https://stackoverflow.com/questions/1010645/what-does-the-registernatives-method-do
wait/notify: https://juejin.im/post/5b612adbe51d4517df151e66
java Object: https://fangjian0423.github.io/2016/03/12/java-Object-method/
网友评论