本文主要讲解“如何基于Java理解对象源代码”。感兴趣的朋友不妨看看。本文介绍的方法简单、快速、实用。让边肖带你学习如何理解基于Java的Object的源代码。
getClass
publicatefinativeclasgetclass();GetClass也是一个原生方法,其功能是返回一个对象的运行时Class,其返回值为class type,Class c=obj . GetClass();通过对象c,我们可以得到这个对象的所有成员Methods,每个成员Methods就是一个方法对象;我们还可以得到对象的所有成员变量,每个成员变量都是一个Field对象;同样,我们也可以得到这个对象的constructor,而这个constructor就是一个Constructor对象。这种方法常用于反射。
hashCode
publicationeinthashcode();哈希方法返回一个哈希值。
默认情况下,返回值是从对象的地址转换而来的。
调用hashCode的同一个对象的返回值相等。
如果两个对象的等号相等,那么hashCode必须相等。
如果两个对象的等号不相等,那么hashCode就不一定相等。
equals
public booleaneequals(Objectobj){ return(this==obj);}equals的实现很简单,它的功能是比较两个对象是否相等,而比较的基础就是它们的内存地址。此外,equals还遵循以下原则:
1.自反性:x . equals(x);//true2,对称性:x . equals(y)=y . equals(x);//true3,传递性:if(x . equals(y)y . equals(z))x . equals(z);//true;4.一致性,只要对象没有被修改,多次调用equals()方法的结果不会改变:x . equals(y)==x . equals(y);//true5,非null,对任何不为null的对象x调用x.equals(null)会导致false:x . equals(null);//false;为什么要重写 hashcode 和 equals ?
因为这两种方法都与对象比较有关,如果要比较程序中的对象,很可能要重写这两种方法。因为equals的默认比较逻辑是比较对象的地址,而且两个对象的内存地址肯定是不一样的,所以无论如何通过eqals比较两个对象肯定会返回false。
然而,在实际编程中,我们经常会遇到重复删除,或者将对象放在有序集中,或者将对象存储在没有重复的集中。此时,如果不重写equals和hashCode,则无法满足要求。
例如,系统中有两个对象,对象A和对象B,它们的名称和ID号完全相同。此时,系统内存中有两个对象,但它们的内容一致且清晰,即一个人同时产生两条重复信息。如果使用默认的equals方法进行比较,这两个对象将永远不会相等,也永远不会作为相同的重复消息进行比较。因此,我们需要重写equals和hashCode方法来达到上述要求。
clone
protected nativeobjectclone()throwsclenotsupportedexception;Clone()是对象的受保护方法,它不是公共的。如果一个类没有显式重写clone(),则其他类不能直接调用此类实例的clone()方法。此外,Clone的评论还提到了一些重要的观点:
>
克隆的对象必须要实现 Cloneable 接口并重写 clone 方法,否则会报 CloneNotSupportedException 异常
clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。
浅拷贝:拷贝对象和原始对象的引用类型引用同一个对象。
深拷贝:拷贝对象和原始对象的引用类型引用不同对象。
关于浅拷贝与深拷贝我们后面再讨论。
toString
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
返回该对象的字符串表示,非常重要的方法
-
getClass().getName(); 获取字节码文件的对应全路径名例如java.lang.Object;
-
Integer.toHexString(hashCode()); 将哈希值转成16进制数格式的字符串。
wait 和 notify
public final void wait() throws InterruptedException { wait(0); } public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } 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) 效果一样。
ublic final native void notify(); public final native void notifyAll();
首先是 notify ,notify 的作用就是随机唤醒在等待队列的某个线程,而 notifyAll 就是唤醒在等待队列的所有线程。
注意:notify 和 wait 方法的使用规范。意思就是这二者必须在 synchronized 修饰的同步方法或同步代码中使用。
Thread.sleep() 和 Object.wait() 有什么区别?
首先,二者都可以暂停当前线程,释放 CPU 控制权。主要的区别在于 Object.wait()在释放 CPU 同时,释放了对象锁的控制。而 Thread.sleep() 没有对锁释放。换句话说 sleep 就是耍流氓,占着茅坑不拉屎。
完整代码
package java.lang; public class Object { /** * 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用 */ private static native void registerNatives(); /** * 对象初始化时自动调用此方法 */ static { registerNatives(); } /** * 返回此Object的运行时类 */ public final native Class<?> getClass(); /** * hashCode的常规协定是: * 1.在java应用程序执行期间,在对同一对象多次调用hashCode()方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。 * 从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 * 2.如果根据equals(object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。 * 3.如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode()方法不要求一定生成不同的整数结果。 * 但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。 */ public native int hashCode(); /** * 这里比较的是对象的内存地址 */ public boolean equals(Object obj) { return (this == obj); } /** * 本地clone方法,用于对象的复制 */ protected native Object clone() throws CloneNotSupportedException; /** * 返回该对象的字符串表示,非常重要的方法 * getClass().getName();获取字节码文件的对应全路径名例如java.lang.Object * Integer.toHexString(hashCode());将哈希值转成16进制数格式的字符串。 */ public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } /** * 不能被重写,用于唤醒一个在因等待该对象(调用了wait方法)被处于等待状态(waiting 或 time_wait)的线程,该方法只能同步方法或同步块中调用 */ public final native void notify(); /** * 不能被重写,用于唤醒所有在因等待该对象(调用wait方法)被处于等待状态(waiting或time_waiting)的线程,该方法只能同步方法或同步块中调用 */ public final native void notifyAll(); /** * 不能被重写,用于在线程调用中,导致当前线程进入等待状态(time_waiting),timeout单位为毫秒,该方法只能同步方法或同步块中调用,超过设置时间后线程重新进入可运行状态 */ public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); } /** * 在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行wait(0)调用一样。 * 当前线程必须拥有此对象监视器。 * 该线程发布对此监视器的所有权并等待,直到其他线程通过调用notify方法或notifyAll方法通知在此对象的监视器上等待的线程醒来, * 然后该线程将等到重新获得对监视器的所有权后才能继续执行。 */ public final void wait() throws InterruptedException { wait(0); } /** * 这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法。 */ protected void finalize() throws Throwable { } }
到此,相信大家对“Java基础之如何理解Object源码”有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/43720.html