所有的java对象都隐式继承了Object类对象。所有的java对象都拥有Object默认的方法。
public final native Class>getClass();//返回字节码文件对象 java反射实现方式之一
public native int hashCode();
public boolean equals(Object obj)
protected Object clone();
public String toString();
public final void notify();
public final void notifyAll();
public final void wait(long timeout);
protected void finalized();
1.equals和hashCode
查看完文档以后我们查看源码,发现hashCode是由native关键字修饰,equals方法则是直接使用==比较了内存地址。
public boolean equals(Object obj) {
return (this == obj);
}
equals和hashCode用来标识对象,可以在非排序的情况下比较两个对象是否相等(对象数组中可以使用比较器)。
那么我们仔细关注一下hashCode源码所给出的注释
/**
* 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}.
*
* The general contract of {@code hashCode} is:
*
* - 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.
*
- 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.
*
- It is not 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.
*
*
* 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™ programming language.)
- 返回一个哈希值给对象,这个方法有益于底层结构是哈希表的结构对象,例如HashMap
- 如果两个对象的equals的结果是相等的,则调用hashCode返回的int也必须是相同的
- 如果两个对象的equals不相等,hashCode可以不相等,但是如果equasl不相等,hashCode也不相等的话,有利于提高散列性能。(Map集合和Set集合底层判断重复的时候,先判断hashCode是否相等,如相等,再判断key的equals是否相等,一旦短路与&&生效,会大大提高程序执行效率。)
- hashCode默认是由对象的地址转换而来,同时根据不同的对象转成不同的hash值(但这种实现不是java语言要求的 所以我们常常重写它)
适用场景:我们在适用自定义对象作为Map/Set键时,为了区分不同对象,必须重写hashCode和equals。
延申:equals和==的区别:
对于基本类型而言,==比较的是内容。
对于引用数据类型而言,==比较的是地址。
对于引用数据类型(String,Integer,date等),重写了equals和hashCode的,比较的就是内容。
对于引用数据类型,没有重写equals和hashCode的,适用的还是Object的equals方法,比较的是地址。
再延申:那我们就去看一看String重写的equals源码吧:
public boolean equals(Object anObject) {
if (this == anObject) {//先判断地址是否相等,地址相等直接返回
return true;
}
if (anObject instanceof String) {//判断是否是String类,为了向下转型的安全
String anotherString = (String)anObject;
int n = value.length;//value就是本String转化成的字符数组
if (n == anotherString.value.length) {//比较两者的字符数组是否相等
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//从前往后一个字符一个字符比较
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
OK,那么我们知道了,String先是判断两个对象的地址是否相等,然后向下转型,再转成字符数组,从后向前遍历比较。
2.toString
/**
* 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.
*
* 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:
*
*
* getClass().getName() + '@' + Integer.toHexString(hashCode())
*
*
* @return a string representation of the object.
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 用文本方式标识一个对象
- Object默认返回的是 字节码文件的名称+@+一个内存地址的int映射
clone()方法
一般java的赋值是复制对象的引用(=),(类初始化状态不一)浅拷贝。要实现深拷贝,(连成员变量初始化状态都一样)成员变量都拷贝出去一份(如果是可变的引用),因而"="是属于浅拷贝。
所以深拷贝是成员变量(如果是可变的引用)都复制一份,浅拷贝则是不复制成员变量。两者初始化程度不一。
clone用法:
- 克隆的对象要实现Cloneable接口
- 重写clone方法,最好修饰成public
范例:浅拷贝Time
public class Time implements Cloneable{
//可变成员变量
private Date date;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
范例:深拷贝Time
public class Time implements Cloneable{
//可变成员变量
private Date date;
@Override
protected Object clone() throws CloneNotSupportedException {
//向下转型 拷贝Time对象
Time time=(Time)super.clone();
//拷贝可变的成员变量
time.date=(Date)date.clone();
//返回拷贝的对象
return time;
}
}
wait和notify方法
这是线程间通信的API
- 无论是wait、notify还是notifyAll()都需要由监听器对象(锁对象)来进行调用,他们都是在同步代码块中调用的,否则会抛出异常!
- notify()唤醒的是在等待队列的某个线程(不确定会唤醒哪个),notifyAll()唤醒的是等待队列所有线程
- 导致wait()的线程被唤醒可以有4种情况
- 该线程被中断
- wait()时间到了
- 被notify()唤醒
- 被notifyAll()唤醒
- 调用wait()的线程会释放掉锁
一些面试题:
为什么wait和notify在Object方法上?
因为锁是对象锁,让线程等待某个对象的锁,应该由对象来操作
notify方法调用后,会发生什么?
会唤醒等待队列的某个线程
注:并不会立刻唤醒,会等notify的synchronized代码块执行完之后才会获得锁对象
Thread.sleep和Object.wait区别?
sleep不会释放对象锁的控制,而wait会。
finalize方法
这是在GC前会被JVM调用的方法,用来对一些特定的内存进行GC,一般不重写,对一些JNI操作的gc会使用。
因而hashCode和equals用于对象比较,clone用于对象克隆,toString用于对象标识,notify与wait是对象锁相关。