Java 基础 | Object 源码解析

开发 后端
Java 是一门面向对象的语言,在 Java 里面一切都可以看作是一个对象,而 Java 里面所有的对象都默认继承于 Object 类。

[[355023]]

 前言

Java 是一门面向对象的语言,在 Java 里面一切都可以看作是一个对象,而 Java 里面所有的对象都默认继承于 Object 类。

本文基于JDK1.8


可以看到一共12个方法,其中 registerNatives() 是 native 修饰的,一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用。

getClass

  1. public final native Class getClass(); 

getClass 也是个 native 方法,这个方法的作用就是返回某个对象的运行时类,它的返回值是 Class 类型,Class c = obj.getClass();通过对象 c ,我们可以获取该对象的所有成员方法,每个成员方法都是一个 Method 对象;我们也可以获取该对象的所有成员变量,每个成员变量都是一个 Field 对象;同样的,我们也可以获取该对象的构造函数,构造函数则是一个 Constructor 对象。这个方法在反射时会常用到。

hashCode

  1. public native int hashCode(); 
  • hashCode 方法返回散列值。
  • 返回值默认是由对象的地址转换而来的。
  • 同一个对象调用 hashCode 的返回值是相等的。
  • 两个对象的 equals 相等,那 hashCode 一定相等。
  • 两个对象的 equals 不相等,那 hashCode 也不一定相等。

equals

  1. public boolean equals(Object obj) { 
  2.     return (this == obj); 

 equals 的实现非常简单,它的作用就是比较两个对象是否相等,而比较的依据就是二者的内存地址。除此之外,equals 还遵循以下几个原则:

  1. 1、自反性:x.equals(x);  // true 
  2. 2、对称性:x.equals(y) == y.equals(x);  // true 
  3. 3、传递性:if (x.equals(y) && y.equals(z)) 
  4.             x.equals(z); // true
  5. 4、一致性,只要对象没有被修改,多次调用 equals() 方法结果不变: 
  6. x.equals(y) == x.equals(y); // true  
  7. 5、非空性,对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false : 
  8. x.equals(null); // false

 为什么要重写 hashcode 和 equals ?

因为这两个方法都跟对象的比较有关,所以如果在程序中要做对象比较,那大概率要重写这两个方法了。因为equals默认的比较逻辑就是对象的地址进行比较,两个对象内存地址肯定不同,所以无论如何两个对象通过eqals比较肯定返回false。

但在实际编程中,我们经常会遇到去重,或者将对象放到有序集合中,或者将对象存入无重复的集合中,这时如果没有重写equals和hashCode,则无法达到需求。

举个例子,系统中同时存在两个对象,对象A和对象B,其姓名和身份证号一模一样。此时,在系统内存中是两个对象,但其内容一致分明是一个人同时产生了两条重复信息。如果使用默认的equals方法比较,则这两个对象永远不相等,永远不能比出来是一条相同的重复信息。所以,要重写equals和hashCode方法来达到以上需求效果。

clone 

  1. protected native Object clone() throws CloneNotSupportedException; 

clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。此外,Clone 的注释中还提到比较重要的几点:

  • 克隆的对象必须要实现 Cloneable 接口并重写 clone 方法,否则会报 CloneNotSupportedException 异常
  • clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。
  • 浅拷贝:拷贝对象和原始对象的引用类型引用同一个对象。
  • 深拷贝:拷贝对象和原始对象的引用类型引用不同对象。

关于浅拷贝与深拷贝我们后面再讨论。

toString

  1. public String toString() { 
  2.  return getClass().getName() + "@" + Integer.toHexString(hashCode()); 

 返回该对象的字符串表示,非常重要的方法

  • getClass().getName(); 获取字节码文件的对应全路径名例如java.lang.Object;
  • Integer.toHexString(hashCode()); 将哈希值转成16进制数格式的字符串。

wait 和 notify

  1. public final void wait() throws InterruptedException { 
  2.      wait(0); 
  3.  
  4. public final native void wait(long timeout) throws InterruptedException; 
  5.  
  6. public final void wait(long timeout, int nanos) throws InterruptedException { 
  7.         if (timeout < 0) { 
  8.             throw new IllegalArgumentException("timeout value is negative"); 
  9.         } 
  10.  
  11.         if (nanos < 0 || nanos > 999999) { 
  12.             throw new IllegalArgumentException( 
  13.                                 "nanosecond timeout value out of range"); 
  14.         } 
  15.  
  16.         if (nanos > 0) { 
  17.             timeout++; 
  18.         } 
  19.  
  20.         wait(timeout); 

 wait 的作用是让当前线程进入等待状态,同时,wait() 也会让当前线程释放它所持有的锁。直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,当前线程被唤醒进入就绪状态。

wait(long timeout) (以毫秒为单位)让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notify() 方法或 notifyAll() 方法,或者超过指定的时间量,当前线程被唤醒进入就绪状态。

wait(long timeout, int nanos) 和 wait(long timeout) 功能一样,唯一的区别是这个可以提供更高的精度。总超时时间(以纳秒为单位)计算为 1000000 *timeout+ nanos。By the way ,wait(0,0) 和 wait(0) 效果一样。

  1. ublic final native void notify(); 
  2. public final native void notifyAll(); 

 首先是 notify ,notify 的作用就是随机唤醒在等待队列的某个线程,而 notifyAll 就是唤醒在等待队列的所有线程。

注意:notify 和 wait 方法的使用规范。意思就是这二者必须在 synchronized 修饰的同步方法或同步代码中使用。

Thread.sleep() 和 Object.wait() 有什么区别?

首先,二者都可以暂停当前线程,释放 CPU 控制权。主要的区别在于 Object.wait()在释放 CPU 同时,释放了对象锁的控制。而 Thread.sleep() 没有对锁释放。换句话说 sleep 就是耍流氓,占着茅坑不拉屎。

完整代码

  1. package java.lang; 
  2.  
  3.  
  4. public class Object { 
  5.  
  6.     /** 
  7.      * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用 
  8.      */ 
  9.     private static native void registerNatives(); 
  10.  
  11.     /** 
  12.      * 对象初始化时自动调用此方法 
  13.      */ 
  14.     static { 
  15.         registerNatives(); 
  16.     } 
  17.  
  18.     /** 
  19.      * 返回此Object的运行时类 
  20.      */ 
  21.     public final native Class<?> getClass(); 
  22.  
  23.     /** 
  24.      * hashCode的常规协定是: 
  25.      * 1.在java应用程序执行期间,在对同一对象多次调用hashCode()方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。 
  26.      * 从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 
  27.      * 2.如果根据equals(object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。 
  28.      * 3.如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode()方法不要求一定生成不同的整数结果。 
  29.      * 但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。 
  30.      */ 
  31.     public native int hashCode(); 
  32.  
  33.     /** 
  34.      * 这里比较的是对象的内存地址 
  35.      */ 
  36.     public boolean equals(Object obj) { 
  37.         return (this == obj); 
  38.     } 
  39.  
  40.     /** 
  41.      * 本地clone方法,用于对象的复制 
  42.      */ 
  43.     protected native Object clone() throws CloneNotSupportedException; 
  44.  
  45.     /** 
  46.      * 返回该对象的字符串表示,非常重要的方法 
  47.      * getClass().getName();获取字节码文件的对应全路径名例如java.lang.Object 
  48.      * Integer.toHexString(hashCode());将哈希值转成16进制数格式的字符串。 
  49.      */ 
  50.     public String toString() { 
  51.         return getClass().getName() + "@" + Integer.toHexString(hashCode()); 
  52.     } 
  53.  
  54.     /** 
  55.      * 不能被重写,用于唤醒一个在因等待该对象(调用了wait方法)被处于等待状态(waiting 或 time_wait)的线程,该方法只能同步方法或同步块中调用 
  56.      */ 
  57.     public final native void notify(); 
  58.  
  59.     /** 
  60.      * 不能被重写,用于唤醒所有在因等待该对象(调用wait方法)被处于等待状态(waiting或time_waiting)的线程,该方法只能同步方法或同步块中调用 
  61.      */ 
  62.     public final native void notifyAll(); 
  63.  
  64.     /** 
  65.      * 不能被重写,用于在线程调用中,导致当前线程进入等待状态(time_waiting),timeout单位为毫秒,该方法只能同步方法或同步块中调用,超过设置时间后线程重新进入可运行状态 
  66.      */ 
  67.     public final native void wait(long timeout) throws InterruptedException; 
  68.  
  69.  
  70.     public final void wait(long timeout, int nanos) throws InterruptedException { 
  71.         if (timeout < 0) { 
  72.             throw new IllegalArgumentException("timeout value is negative"); 
  73.         } 
  74.  
  75.         if (nanos < 0 || nanos > 999999) { 
  76.             throw new IllegalArgumentException( 
  77.                     "nanosecond timeout value out of range"); 
  78.         } 
  79.  
  80.         if (nanos > 0) { 
  81.             timeout++; 
  82.         } 
  83.  
  84.         wait(timeout); 
  85.     } 
  86.  
  87.     /** 
  88.      * 在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行wait(0)调用一样。 
  89.      * 当前线程必须拥有此对象监视器。 
  90.      * 该线程发布对此监视器的所有权并等待,直到其他线程通过调用notify方法或notifyAll方法通知在此对象的监视器上等待的线程醒来, 
  91.      * 然后该线程将等到重新获得对监视器的所有权后才能继续执行。 
  92.      */ 
  93.     public final void wait() throws InterruptedException { 
  94.         wait(0); 
  95.     } 
  96.  
  97.     /** 
  98.      * 这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法。 
  99.      */ 
  100.     protected void finalize() throws Throwable { 
  101.     } 

 PS:这里有一个技术交流群(扣扣群:1158819530),方便大家一起交流,持续学习,共同进步,有需要的可以加一下。

 

责任编辑:姜华 来源: 今日头条
相关推荐

2021-03-24 07:16:57

RocketMQ源码解析Topic

2015-09-16 09:10:27

Java源码解析

2021-02-08 08:45:18

Java内部类Object类

2015-09-11 09:17:55

JavaJava HashMa

2021-07-03 08:51:30

源码Netty选择器

2017-04-05 20:00:32

ChromeObjectJS代码

2020-10-20 10:17:20

Java泛型Type

2022-05-20 10:32:49

事件循环器事件队列鸿蒙

2009-11-02 14:55:52

VB.NET Obje

2013-06-08 10:11:31

Java线程池架构

2015-10-10 09:39:42

Java线程池源码解析

2021-02-20 06:09:46

libtask协程锁机制

2012-11-06 11:07:59

jQueryJSjQuery框架

2021-07-09 06:48:30

注册源码解析

2024-01-18 08:31:22

go实现gorm框架

2021-05-26 11:30:24

Java线程池代码

2020-01-12 19:10:30

Java程序员数据

2010-10-29 09:34:16

2011-05-10 10:00:41

克隆

2017-01-11 14:19:26

JVM源码All
点赞
收藏

51CTO技术栈公众号