Object详解

作者: alonwang | 来源:发表于2019-06-29 10:33 被阅读0次

Object的方法列表如下.

Untitled-1c0375dd-9bb0-4900-b36a-367d6fa5bb5b.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

生成对象的哈希码,有如下约定:

  1. 如果两个对象根据equals比较相等,那么这两个对象的哈希码也相等(反之并不成立)
  2. 如果两个对象根据equals比较并不相等,不要求两个对象的哈希码不同(但是如果不同可以提高哈希表的性能)

对于Object而言,equals直接比较两个对象的内存地址,hashCode则是根据内存地址计算. 这符合Object的定义: 内存地址相同的对象才是相等的.

哈希表对equals和hashCode的要求

如果对象需要作为哈希表的key,就需要特别关注equals和hashCode的关系,先简单介绍下HashMap.get(key)的过程:

  1. 首先根据key的哈希码确定key在entry数组中的位置entry[i]

  2. 如果entry[i]为空,说明不存在,如果不为空,需要通过equals判断依次判断key和entry[i]链表中的key是否相等,如果相等,说明存在,否则说明不存在.

为了实现上面的过程,当equals与hashCode结合时,有如下关系:

  1. 必须同时覆盖equals和hashCode
  2. equals相等时,hashCode必须相等
  3. 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 — 对象的终结

一个对象真正被回收,至少需要经过两次标记

  1. 对经过可达性分析无法到达的对象进行第一次标记并筛选,如果对象覆盖了finalize方法且finalize方法没有被虚拟机调用过,有必要执行finalize方法,否则没有必要执行finalize方法,直接清理
  2. 如果对象被判定为有必要执行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/

相关文章

  • 详解Object.defineProperty方法

    详解Object.defineProperty方法 Object.defineProperty()会直接在一个对象...

  • Object.defineProperty() 不详解

    Object.defineProperty()不详解 vue用Object.defineProperty手写一个简...

  • UE4 Blueprint 类型详解

    Blueprint 类型详解 Actor: An actor is an object that can be p...

  • 【总结】2017.03.03

    2017.03.03 - 计划 完善hM侧边栏组件化 Object对象 Object详解 - 实际完成 完善hM侧...

  • Object详解

    Object的方法列表如下. registerNatives — 自定义native方法命名 registerNa...

  • Object详解

    标准形式 原型 Object.prototype 标准方法 Object.assign() 前拷贝 Object....

  • runtime详解

    runtime详解 1,runtime的核心—消息传递 在[object foo]在运行的过程中会给object发...

  • Java编程基础(11)

    Java编程基础知识:Java内置包装内 1.Java Object类详解:Object类的常用方法(equals...

  • Object类详解

    Object位于java.lang中,以大众父亲闻名,是java所有类的父类,数组也继承了Object,不过,接口...

  • Object类详解

    Object是所有类的父类,任何类都默认继承Object。Object是类层次结构的根类。每个类都使用Object...

网友评论

    本文标题:Object详解

    本文链接:https://www.haomeiwen.com/subject/ywiucctx.html