一.equals方法
Object中的equals方法用来检测一个对象是否等于另一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。这样看,将其作为默认操作也是合情合理的。然而,对于大多类来说,这种判断没有什么意义,所有我们常常需要重写equals方法。
下面是一个实现比较雇员对象是否相等的equals方法(一个雇员对象假设它有姓名,薪水,雇佣日期信息,如果这些信息都相同的话,就认为它们是相等的):
class Employee{
private String name;
private double salary;
private Date hireDay;
//...
public boolean equals(Object otherObject){
if(this == otherObject){
return true;//如果两个对象相等,就直接返回true
}
if(otherObject == null){
return false;//如果otherObject为空,则直接返回false
}
if(getClass() != otherObject.getClass()){
return false;//只有两个对象数以同一类时,才有可能相等
}
//开始测试对象的域是否相等
Employee other = (Employee) otherObject;
return Objects.equals(name,other.name)
&& salary = other.salary
&& Other.equals(hireDay,other.hireDay);
/*使用Other.equals(a,b)是为了防备name和hireDay可能为null的
情况。当a,b都为空时,返回true,当只有一个为空时返回false。*/
在子类中定义equals方法时,首先调用超类的equals。如果检测失败,对象就不可能相等。如果超类中的所有域都相等,再比较子类中的域。
但是如果隐式和显式的参数不属于同一个类,这时候该如何定义equals呢?
在Java语言规范中要求equals方法应该遵循下面这些特性:
(1)自反性:对于任何非空引用x,x.equals(x)要求返回true;
(2)对称性:对于任何的引用x和y,x.equals(y)的结果和y.equals(x)的结果应该是一样的;
(3)传递性:对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true;
(4)一致性,如果equals中的对象没有发生变化,那么重复调用equals方法得到的结果应该是不变的;
(5)对于任何非空引用x,x.equals(null)应该返回false。
如何比较两个类是否属于同一个类?
(1)如果equals的语义在每个子类中有所改变,就使用getClass检测;
(2)如果所有的子类都有统一的语义,就使用instanceOf检测。
注意:如果重新定义equals方法,也需要重新定义hashCode方法。
当两个对象使用equals方法结果为true时,它们hashCode必须相等,但hashCode相等时,两个对象却不一定相等。
二.clone方法
当拷贝一个变量时,原始变量与拷贝变量引用同一对象。这就是说,改变一个变量的所引用的对象会对另一个对象产生影响。
默认的克隆操作是浅拷贝,它并没有克隆包含在对象中的内部对象。
如果要实现深拷贝,必须克隆所有可变的实例域:
下面是一个建立深拷贝的例子(还是上面那个雇员对象的例子):
class Employee implements Cloneable{
// ...
public Employee clone() throws CloneNotSupportedException{
/*只要在clone中含有没有实现Cloneable接口的对象,clone方法就会抛出
一个异常,虽然代码里的对象都实现了Clonable接口,但是编译器并不知情。
并为了其他类可以使用该clone方法,将可见域改为public。*/
Employee cloned = (Employee) super.clone();
clone.hireDay = (Date) hireDay.clone();
return cloned;
}
}