作为所有类的顶层父类,没想到Object的魔力如此之大! ...

打印 上一主题 下一主题

主题 923|帖子 923|积分 2769

写在开头

在上一篇博文中我们提到了Java面向对象的四大特性,其中谈及“抽象”特性时做了一个引子,引出今天的主人公Object,作为所有类的顶级父类,Object被视为是James.Gosling的哲学思考,它高度概括了事务的自然与社会行为。
源码分析

跟进Object类的源码中我们可以看到,类的注释中对它做了一个总结性的注释。

在Object的内部主要提供了这样的11种方法,大家可以在源码中一个个的跟进去看,每个方法上均有详细的英文注释,养成良好的看英文注释习惯,是一个合格程序员的必备基础技能哈。
  1. /**
  2. * 方法一
  3. */
  4. public final native Class<?> getClass()
  5. /**
  6. * 方法二
  7. */
  8. public native int hashCode()
  9. /**
  10. *方法三
  11. */
  12. public boolean equals(Object obj)
  13. /**
  14. * 方法四
  15. */
  16. protected native Object clone() throws CloneNotSupportedException
  17. /**
  18. * 方法五
  19. */
  20. public String toString()
  21. /**
  22. * 方法六
  23. */
  24. public final native void notify()
  25. /**
  26. * 方法七
  27. */
  28. public final native void notifyAll()
  29. /**
  30. * 方法八
  31. */
  32. public final native void wait(long timeout) throws InterruptedException
  33. /**
  34. * 方法九
  35. */
  36. public final void wait(long timeout, int nanos) throws InterruptedException
  37. /**
  38. * 方法十
  39. */
  40. public final void wait() throws InterruptedException
  41. /**
  42. * 方法十一
  43. */
  44. protected void finalize() throws Throwable { }
复制代码
getClass()

getClass()是Java的一个native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。在源码中我们可以到,该方法的返回是Class类。
Class 类存放类的结构信息,能够通过 Class 对象的方法取出相应信息:类的名字、属性、方法、构造方法、父类、接口和注解等信息。
hashCode()

同样是native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
equals()

默认比较对象的地址值是否相等,子类可以重写比较规则,如String 类对该方法进行了重写以用于比较字符串的值是否相等。
clone()

native 方法,用于创建并返回当前对象的一份拷贝。
toString()

返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。
notify()

native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
notifyAll()

native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
wait(long timeout)

native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
wait(long timeout, int nanos)

多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒。
wait()

让持有对象锁的线程进入等待,不可设置超时时间,没有被唤醒的情况下,会一直等待。
finalize()

实例被垃圾回收器回收的时候触发的操作
高频面试考点总结

虽然在日常的代码开发中,我们很少会直接使用Object类,但考虑到它的独特地位,与此相关的面试考点还是不少的,我们今天总结一下。
1.浅拷贝、深拷贝、引用拷贝的区别?

浅拷贝:基本类型的属性会直接复制一份,而引用类型的属性复制:复制栈中的变量和变量指向堆内存中的对象的指针,不复制堆内存中的对象,也就是说拷贝对象和原对象共用同一个内部对象。

深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

引用拷贝:简单来说,引用拷贝就是两个不同的引用指向同一个对象。

2.Java中如何实现浅拷贝与深拷贝

其实实现浅拷贝很简单,实现 Cloneable 接口,重写 clone() 方法,在clone()方法中调用父类Object的clone()方法。
  1. public class TestClone {
  2.     public static void main(String[] args) throws CloneNotSupportedException {
  3.         Person p1 = new Person(1, "ConstXiong");//创建对象 Person p1
  4.         Person p2 = (Person)p1.clone();//克隆对象 p1
  5.         p2.setName("其不答");//修改 p2的name属性,p1的name未变
  6.         System.out.println(p1);
  7.         System.out.println(p2);
  8.     }
  9.    
  10. }
  11. /**
  12. * person类
  13. */
  14. class Person implements Cloneable {
  15.    
  16.     private int pid;
  17.    
  18.     private String name;
  19.    
  20.     public Person(int pid, String name) {
  21.         this.pid = pid;
  22.         this.name = name;
  23.         System.out.println("Person constructor call");
  24.     }
  25.     public int getPid() {
  26.         return pid;
  27.     }
  28.     public void setPid(int pid) {
  29.         this.pid = pid;
  30.     }
  31.     public String getName() {
  32.         return name;
  33.     }
  34.     public void setName(String name) {
  35.         this.name = name;
  36.     }
  37.     @Override
  38.     protected Object clone() throws CloneNotSupportedException {
  39.         return super.clone();
  40.     }
  41.     @Override
  42.     public String toString() {
  43.         return "Person [pid:"+pid+", name:"+name+"]";
  44.     }
  45.    
  46. }
复制代码
那么如何实现深拷贝呢,这里给出两种方法
方法一:将对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。
方法二:结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝。
3.==和equals的区别是什么?
  1. **区别**
  2. == 是关系运算符,equals() 是方法,结果都返回布尔值
  3. Object 的 == 和 equals() 比较的都是地址,作用相同
  4. **== 作用:**
  5. 基本类型,比较值是否相等
  6. 引用类型,比较内存地址值是否相等
  7. 不能比较没有父子关系的两个对象
  8. **equals()方法的作用:**
  9. JDK 中的类一般已经重写了 equals(),比较的是内容
  10. 自定义类如果没有重写 equals(),将调用父类(默认 Object 类)的 equals() 方法,Object 的 equals() 比较使用了 this == obj
  11. 可以按照需求逻辑,重写对象的 equals() 方法(重写 equals 方法,一般须重写 hashCode 方法)
复制代码
4.为什么说重写equals方法也要重写hashCode方法呢?

equals()方法是用来判断两个对象是否相等的重要方法,Object中默认比较地址,但这在实际使用上意义不大,比如两个字符串,我们比较的初衷肯定是他们的字符串内容是否相等,而不是内存地址,典型的就是String内部的重写equals。
hashCode()方法是一个C或C++实现的本地方法,用以获取对象的哈希码值(散列码),通过码值可以确定该对象在哈希表中的索引位置,是通过线程局部状态来实现的随机数值。子类可通过重写该方法去重新设计hash值。
使用hashCode方法可以一定程度上判断两个对象是否相等,因为,若两个对象相等,那么他们所在的索引位置肯定就一样,这时hashCode获取的哈希码自然也就一样,但这个条件反过来就不一定成立了,哈希码相等的两个对象不一定相等,因为存在哈希碰撞
看完这两个方法的特点,我们大概可以明白了,确保两个对象是否真正相等,需要这个两个方法的协作,equals是逻辑上的相等,hashCode是物理上的相等,若我们在重写equals()方法时,不去重写配套的hashCode方法,就会导致两个对象在逻辑上相等,但物理上不等,这会带来很多问题,譬如集合类HashMap的底层实现是数据+链表/红黑树的方式,通过计算hash寻找位置,通过equals判断元素相等,这时候若仅重写equals的话,hash不重写,就会出现逻辑上我们认为相等的两个数,存在了不同的位置上,造成混乱的场面。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表