此文章为我阅读《java核心技术I》的读书笔记。
穿插个人理解和书上所述。
Objecet类是java中所有类的始祖,如果没有明确指出超类,那么Object就被认为是这个类的超类,在java中,每个类都是由Object扩展而来的。在java中,只有基本类型不是对象,如,数值,字符和布尔类型的值都不是对象。
1.重写equals方法
首先,我们先要清楚equals方法是做什么的?
Object类中的equals方法用于检测一个对象是否等于另一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,那么它们一定是相等的。从这里看,似乎是废话,而且也没有什么意义。然而,经常需要检测两个对象的状态的相等性,如果两个对象的状态是相等的,就认为这两个对象是相等的。 比如说,如果两个雇员对象的姓名,薪水和雇佣日期都是相等的,那么可以认为他们是相等的。那么这个时候就要重写equals方法了。下面给出一个例子:
public class People{
...
public boolean equals(Object otObj){
if(this == otObj) return true; //检测是否引用同一个对象
if(otObj == null) return false; // 如果这个对象变量没有与任何对象关联
if(getClass() != otObj.getClass()) return false; //检测是否属于同一个类
Student other = (Student) otObj;
return Object.equals(name, other.name)
&& salary == other.salary
&& Object.equals(hireDay, other.hireDay);
}
}
这段代码中的:
Object.equals(name, other.name)
&& salary == other.salary
&& Object.equals(hireDay, other.hireDay);
是为了防备name和hireDay都为null的情况,因此调用Object.equals方法,如果两个参数都为null,返回true。只要其中一个为null,则返回false。如果两个都不为空,那么调用a.equals(b)。
在子类中定义equals方法时,首先要调用超类的equals。如果检测失败,对象就不可能相等。如果超类中的域都相等,就需要比较子类中的实例域。
如:
public class Manager extends Employee
{
public boolean equals(Object othObj)
{
if(!super.equals(othObj)) reutrn false;
Manager other = (Manager) othObj;
return bonus == other.bonus;
}
}
java语言规范要求equals方法具有下面的特性:
<1>自反性: 对于任何非空引用x,x.equals(x)应该返回true。
<2>对称性:对于任何引用x和y,当且仅当x.equals(y) 返回true,y.equals(x)也应该返回true。
<3>传递性: 对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。
<4>一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。
下面给出编写一个完美的equals方法的建议:
<1>显式参数命名为otherObject,等会将它转化成other的变量。
<2>检测this和otherObject是否引用同一个对象。
if(this == otherObject) return true;
<3>检测otherObject是否为null,如果为null,那么返回false。
if(otherObject == null) return false;
<4>比较this和otherObject是否属于同一个类。如果equals的语义在每个子类都有所改变,就是用getClass检测:
if(getClass() != otherObject.getClass()) return false;
如果所有的子类都拥有统一的语义,就是用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
<5>将otherObject转换为相应的类类型变量:
ClassName other = (ClassName) otherObject;
<6>现在开始对所有需要比较的域进行比较了。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true。否则就返回false。
最后,如果在子类中重新定义equals,就要在其中包含调用super.equals(other)。
提示:对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。
2.重写hashCode方法
散列码(hash code)是由对象导出的一个整形值。散列码是没有规律的。如果x,y是两个不同的对象,那么x.hashCode()与y.hashCode()基本不会相同。
由于hashCode方法定义在Object类中,因此每个对象,都有一个默认的散列码,其值为对象的存储地址。
如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入散列表中。
Equals 与hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。
提示:如果存在数组类型的域,那么就可以使用静态的Arrays.hashCode方法计算一个散列码,这个散列码由数组元素的散列码组成。