一文带你读懂Object类源码

2022-05-28 12:20:23 浏览数 (1)

Object类是一个比较特殊的类,是所有类的超级父类,java中如果一个类没有用 extends关键字 明确指出继承于某个类,那么它默认继承Object类。下面我们一起分析这个默默被所有类所继承的 Object。

Object 设计要点

Object 类是Java中所有类的父类,作为最重要的基类,它提供了12个默认实现方法(jdk8)。

在解析每个方法的功能实现和用途之前,我们需要理解几个概念:

  • Monitor:java中每个对象都有唯一的一个monitor,在Java的设计中,每一个对象自打娘胎里出来,就带了一把看不见的锁,通常我们叫“内部锁”,或者“Monitor锁”。
  • 对象锁池:每个java对象都拥有两个池,分别为锁池(EntrySet)和(WaitSet)等待池。

锁池:假如已经有线程A获取到了锁,这时候又有线程B需要获取这把锁(比如需要调用synchronized修饰的方法或者需要执行synchronized修饰的代码块),由于该锁已经被占用,所以线程B只能等待这把锁,这时候线程B将会进入这把锁的锁池。 等待池:假设线程A获取到锁之后,由于一些条件的不满足(例如生产者消费者模式中生产者获取到锁,然后判断队列为满),此时需要调用对象锁的wait方法,那么线程A将放弃这把锁,并进入这把锁的等待池。

  • Native 方法:本地方法,基于C/C 实现的方法。本地方法用法可以参考文章《谨慎的使用本地方法》
  • static 方法块:static{} 静态代码块会在JVM加载类之前执行。执行优先级高于非静态的初始化块,它会在类初始化的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员。
  • protected 方法:对于任何继承此类的导出类 或 其他任何位于同一个包内的类,方法是可以访问的。

常用方法列表

在jdk8中,Object 类的方法列表如下:

方法名称

功能

备注

clone

克隆对象

浅复制(基础类型数据与复合对象的引用会被直接拷贝)

equal

比较对象

默认依赖hashCode()方法

finalize

告诉GC可以回收该对象,但真正回收时机得交由GC来判断

getClass

获取运行时类型

hashCode

获取对象哈希Code

notify

唤醒该对象的放在等待池的某个线程

notifyAll

唤醒该对象的等待池的所有线程

toString

返回标识对象的字符串,默认格式(getClass().getName() '@' Integer.toHexString(hashCode()))

wait()/wait(long)/wait(long, int)

让出目标对象监听器,使当前线程将自己置于该目标对象的等待池中

registerNatives

将class里面native的方法的地址 1指向执行的c代码的函数地址

方法1:clone()

Object类的 clone()方法,实现了对象中各个属性的复制,但它的可见范围是protected的,所以实体类使用克隆的前提是:

  1. 实现Cloneable接口,这是一个标记接口,自身没有方法。
  2. 覆盖clone()方法,可见性提升为public。
代码语言:javascript复制
public class TestClone {
  public static void main(String[] args) throws CloneNotSupportedException {
    Address address=new Address();
    address.setType("Home");
    address.setValue("北京");
    Personclone p1=new Personclone();
    p1.setAge(31);
    p1.setName("Peter");
    p1.setAddress(address);
    Personclone p2=(Personclone) p1.clone();
    System.out.println("p1 == p2:"   (p1==p2));
    System.out.println("p1=" p1);
    System.out.println("p2=" p2);
  }
}
@Data
class Personclone implements Cloneable {
  private String name;
  private Integer age;
  private Address address;
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }
}

@Data
class Address {
  private String type;
  private String value;
}

输出结果:

代码语言:javascript复制
p1 == p2:false
p1=Personclone(name=Peter, age=31, address=Address(type=Home, value=北京))
p2=Personclone(name=Peter, age=31, address=Address(type=Home, value=北京))

方法2:equal()

Object 类默认实现的 equal():“==”比较两个变量本身的值,即两个对象在内存中的首地址。

源码:

代码语言:javascript复制
 public boolean equals(Object obj) {
        return (this == obj);
 }

例子:

代码语言:javascript复制
public class TestEqual {
  public static void main(String[] args) {
    Address address1 = new Address();
    Person person = new Person(11);
    System.out.println("address1.equals(person):"   address1.equals(person));
  }
}

输出结果:

代码语言:javascript复制
address1.equals(person):false

方法3:finalize()

Object 类的finalize() 作用是“呼叫”垃圾回收器:该对象已经没有被任何地方所引用了,可以被回收。

Java 有GC 负责回收无用对象占据的内存资源,但也有特殊情况:假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于GC 只知道释放经过new方式分配的内存,所以它不知道该如何释放该对象的“特殊”内存。所以,为了应对这种情况,Java运行在类中定义一个 finalize() 的方法。

finalize() 方法什么时候被调用?

垃圾回收器(garbage collector)决定回收某对象时,就会运行该对象的finalize()方法;而System.gc()与System.runFinalization()方法增加了finalize方法执行的机会,但不可盲目依赖它们(避免使用终结方法)。

下面是使用覆盖使用 finalize() 方法的案例:

代码语言:javascript复制
/**
 * REMARK
 *  JVM : 一次对象的自我拯救演示
 *  finalize 方法:任何一个对象的finalize方法都只会被系统自动调用一次(尽量避免使用它)
 */
public class FinalizeEscapeGC {

    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive(){
        System.out.println("yes, i am still alive:) ");
    }

    @Override
    protected void finalize() throws Throwable {
        // 覆盖实现finalize方法
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC();

        // 对象第一次拯救自己
        SAVE_HOOK = null;
        System.gc(); //触发使用finalize()

        // 因为finalize方法优先级很低,所以暂停 0.5s 等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("first time check,no, i am dead :(");
        }

        // 下面这段代码和上面的完全相同,但是这次自救却失败了
        SAVE_HOOK = null;
        System.gc();
        // 因为finalize方法优先级很低,所以暂停 0.5s 等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("second time check, no, i am dead :(");
        }

    }
}

输出结果:

代码语言:javascript复制
finalize method executed!
yes, i am still alive:) 
second time check, no, i am dead :(

方法4:getClass()

Object 类的 getClass() 返回运行时类信息。

代码语言:javascript复制
    /**
     * Returns the runtime class of this {@code Object}. The returned
     * {@code Class} object is the object that is locked by {@code
     * static synchronized} methods of the represented class.
     *
     * <p><b>The actual result type is {@code Class<? extends |X|>}
     * where {@code |X|} is the erasure of the static type of the
     * expression on which {@code getClass} is called.</b> For
     * example, no cast is required in this code fragment:</p>
     *
     * <p>
     * {@code Number n = 0;                             }<br>
     * {@code Class<? extends Number> c = n.getClass(); }
     * </p>
     *
     * @return The {@code Class} object that represents the runtime
     *         class of this object.
     * @jls 15.8.2 Class Literals
     */
    public final native Class<?> getClass();

那么Class类是什么呢?

在Java中用来表示运行时类型信息的对应类就是Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。类信息包括了:类名称、包路径、构造器、字段属性、方法、父类结构等等。我们通常使用的反射就用到了Class类。

方法5:hashCode()

Object 类的 hashCode() 返回标识当前对象的唯一hash值。

代码语言:javascript复制
   /**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();

hashCode() 方法应用场景非常多,比如HashMap:在jdk8中,HashMap.put(Key K, Value V) ,将节点添加到链表或者红黑树时,会利用对象的hashcode 判断是否冲突。

代码语言:javascript复制
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

方法6~7:notify()/notifyAll()

Object 类的 notify()/notifyAll 会从等待获取该对象Monitor锁的线程池中,唤醒一个/全部线程,它们是本地final方法,无法被重写。

代码语言:javascript复制
 /**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     * <p>
     * The awakened thread will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened thread will
     * compete in the usual manner with any other threads that might be
     * actively competing to synchronize on this object; for example, the
     * awakened thread enjoys no reliable privilege or disadvantage in being
     * the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
     * <p>
     * Only one thread at a time can own an object's monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notifyAll()
     * @see        java.lang.Object#wait()
     */
    public final native void notify();

方法8:toString()

Object 类的 toString() 会默认返回当前对象hashcode的十六进制字符串。

代码语言:javascript复制
 /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p>
     * The {@code toString} method for class {@code Object}
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `{@code @}', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName()   '@'   Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return  a string representation of the object.
     */
    public String toString() {
        return getClass().getName()   "@"   Integer.toHexString(hashCode());
    }

方法9~11:wait()/wait(long)/wait(long, int)

Object 的 wait()/wait(long)/wait(long timeout, int nanos) 方法让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过参数 timeout 与 nanos 设置的超时时间。

方法12:registerNatives()

registerNatives()方法存在于Object类、Class类、ClassLoader类等常用的类中。

代码语言:javascript复制
 private static native void registerNatives();
    static {
        registerNatives();
    }

Native method是由非Java语言实现的方法。通常Java中的native方法的常是C/C 实现,Java中提供了与其他语言通信的API即JNI(Java Native Interface)。如果要使用Java调用其它语言的函数,就必须遵循JNI的API约定。

总结

Object 类位于 java.lang 包中,编译时会自动导入。了解Object 超类,有助于阅读JDK源码,理解多线程机制。

—END—

0 人点赞