写点什么

就这?Object 类一点不难理解

发布于: 2021 年 01 月 29 日
就这?Object类一点不难理解

1、写在开头

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

2、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 方法:对于任何继承此类的导出类 或 其他任何位于同一个包内的类,方法是可以访问的。

3、Object 常用方法列表

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



3.1、方法 1:clone()

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

  1. 实现 Cloneable 接口,这是一个标记接口,自身没有方法。 

  2. 覆盖 clone()方法,可见性提升为 public。



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); }}@Dataclass Personclone implements Cloneable { private String name; private Integer age; private Address address; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}
@Dataclass Address { private String type; private String value;}
复制代码


输出结果:



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


3.2、方法 2:equal()

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


源码:


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


举例子:


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)); }}
复制代码


输出结果:


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


3.3、方法 3:finalize()

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


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


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

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


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



/** * 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 :("); }
}}
复制代码


输出结果:



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


3.4、方法 4:getClass()

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

    /**     * 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类。

3.5、方法 5:hashCode()

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


 /**     * 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 判断是否冲突。



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


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

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


/** * 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();
复制代码


3.7、方法 8:toString()

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

 /**     * 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());    }
复制代码


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

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

3.9、方法 12:registerNatives()

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

private static native void registerNatives();    static {        
registerNatives();
}
复制代码


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

4、总结

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

5、延伸阅读

《源码系列》

JDK之Object 类

JDK之BigDecimal 类

JDK之String 类

JDK之Lambda表达式


《经典书籍》

Java并发编程实战:第1章 多线程安全性与风险

Java并发编程实战:第2章 影响线程安全性的原子性和加锁机制

Java并发编程实战:第3章 助于线程安全的三剑客:final & volatile & 线程封闭


《服务端技术栈》

《Docker 核心设计理念

《Kafka史上最强原理总结》

《HTTP的前世今生》


《算法系列》

读懂排序算法(一):冒泡&直接插入&选择比较

《读懂排序算法(二):希尔排序算法》

《读懂排序算法(三):堆排序算法》

《读懂排序算法(四):归并算法》

《读懂排序算法(五):快速排序算法》

《读懂排序算法(六):二分查找算法》


《设计模式》

设计模式之六大设计原则

设计模式之创建型(1):单例模式

设计模式之创建型(2):工厂方法模式

设计模式之创建型(3):原型模式

设计模式之创建型(4):建造者模式



发布于: 2021 年 01 月 29 日阅读数: 14
用户头像

Diligence is the mother of success. 2018.03.28 加入

公众号:后台技术汇 笔者主要从事Java后台开发,喜欢技术交流与分享,保持饥渴,一起进步!

评论

发布
暂无评论
就这?Object类一点不难理解