天天看点

复写Object超类的equals,hashCode,toString方法

我们知Object类是Java中所有类的始祖,在Java中每个类都是由他发展而来的,但是却不需这样写:

class Employee extends Object   
           

因为如果没有确切的指出类的超类,Object就会被认为是这个类的超类。根据继承的规则,可以使用Object类型的变量引用任何类的对象:

Object obj = new Student (“lily”,17);
           

当然,Object类型的变量只能用于作为各种值的通用执行者。要想对其中的内容进行具体操作,还需要清楚对象的原始类型,并进行还原。

Student stu = (Student) obj ;
           

Object类提供了三个常用的方法:equals ,hashCode ,toString方法,但是超类提供的方法可能并不能满足子类的要求,需要进行方法的复写。

(1)equals方法

Object 类中的equals方法用于检测一个对象是否等于另一个对象。在Object类中,这个方法将判断两个对象是否为相同的引用。如果两个对象具有相同的引用则判断相等。从这点上来看,这点对于Object类的操作来说倒也合情合理,但是对于继承他的大对数类来说可能并不适用。例如,如果两个Student对象,如果他们的姓名,学号一致就认为他们相等。

class Employee
{
   .... 
   public boolean equals(Object otherObject)
	{
		//同一个引用返回真
		if (this == otherObject)
			return true;
		
		//当对象为null时返回假
		if(otherObject == null)
			return false;
		
		//如果两个对象不属于同一个类就返回假
		if(this.getClass() != otherObject.getClass())//getclass方法用来返回一个对象所属的类
			return false;
		
		Employee other = (Employee) otherObject;
		//当对象的属性一致时返回真
		return Objects.equals(name, other.name) && salary == other.salary &&
				Objects.equals(hireDay, other.hireDay);
	}
} 
           

在子类中定义equals方法时,首先调用超类的equals方法,如果检测失败则对象就不可能相等,如果超类的域都相等,在根据子类中新添加的域进行检测;

public class Manager extends Employee {
    ......
     public boolean equals(Object otherObject)
	{
		//super.equals 方法检查otherObject除了bonus属性之外是否相等
		if (!super.equals(otherObject))
			return false;
		
		Manager manager = (Manager) otherObject;
		return bonus ==manager.bonus;
	}
}
           

Java中对于equals方法的要求如下:

   1、自反性:对于任何非空引用x,x.equals(x)返回true。

    2、对称性: 对于任何引用x和y,当且仅当y.equals(x)返回true,则x.equals(y)也要返回true;

   3、 传递性: 对于任何引用x,y,z,如果x.equals(y)返回为ture,y.equals(z)返回为true,则x.equals(z)也应该返回true;

   4、一致性:如果x,y没做任何变化,反复调用x.equals(y)应该返回同样的结果。

   5、对于任何非空引用x,x.equals(null)都应该返回false;

ps:Java中对于  ==  的要求为,等号左右两边为同一引用对象为真。域相等的且不为同一引用为假。

编写一个较为完美的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是否同属一类,可通过getClass方法检测,

if(this.getClass() != otherObject.getClass())
	return false;
           

如果所有子类都有统一的语义,就使用instanceof检测:

if(!(otherObject instanceof ClassName))   return false
           

5、将otherObject转化为相对应的类类型变量

Employee other = (Employee) otherObject;
           

6、对需要比较的域进行比较。使用 ==进行基本类型的比较,使用equals进行对象域的比较。如果所有的域都匹配,就返回true,否则返回false。

return Objects.equals(name, other.name) && salary == other.salary &&Objects.equals(hireDay, other.hireDay);
           

(2)hashCode方法

散列码(hash code)是由对象导出的一个整型值。散列码是没有规律的。如果x,y是两个不同的对象,一般来说,x.hashCode()与y.hashCode()的值会不同。由于hashCode定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。

值得注意的是,如果重新定义了equals方法就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。

hashCode方法应该返回一个整型的数值(可能为负值),并合理的组织实例域的散列码,以便能够让各个不同对象产生的散列码更加均匀。

public int hashCode()
	{
            //需要组合多个散列值时,可以调用Objects.hash并提供多个参数
           return Objects.hash(name, salary, hireDay);
	}
           

equals与hashCode的定义要一致,如果x.equals(y)为true,则x.hashCode()的值就必须要与y.hashCode()的值相等。

(3)toString方法

    由于Object类中自带的tostring方法可能并不能满足子类的需求,通常在子类中需要重新定义类的toString方法。

 随处可见tostring方法的一个主要原因是,只要对象与一个字符串通过操作符“+”进行操作,Java就会自动调用对象的toString方法。

如果x为一个对象,执行System.out.println(x),就会自动将x转化为x.toString(),并打印出得到的字符串。

public String toString()
	{
		return "name"+ name+ "salary"+ salary+ "hireDay"+ hireDay;
	}
           

下面程序实现了Employee类和Manager类的equals、hashode、toString方法;

package equals_hashCode_toString;


import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;

import commonly_class.string;

public class Employee {

	private String name;
	private double salary;
	private Date hireDay;
	
	public Employee(String name, double salary,int year, int month, int day)
	{
		this.name = name;
		this.salary = salary;
		GregorianCalendar gregorianCalendar = new GregorianCalendar(year, month-1, day);
		hireDay = gregorianCalendar.getTime();
	}

	public String getName()
	{
		return name;
	}

	public double getSalary() 
	{
		return salary;
	}

	public Date getHireDay() 
	{
		return hireDay;
	}
	
	public void raiseSalary(double byPrecent)
	{
		double raise = salary * byPrecent /100;
		salary = salary + raise;
	}
	
	public boolean equals(Object otherObject)
	{
		//同一个引用返回真
		if (this == otherObject)
			return true;
		
		//当对象为null时返回假
		if(otherObject == null)
			return false;
		
		//如果两个对象不属于同一个类就返回假
		if(this.getClass() != otherObject.getClass())
			return false;
		
		Employee other = (Employee) otherObject;
		//当对象的属性一致时返回真
		return Objects.equals(name, other.name) && salary == other.salary &&
				Objects.equals(hireDay, other.hireDay);
	}
	
	public int hashCode()
	{
		return Objects.hash(name, salary, hireDay);
	}
	
	public String toString()
	{
		return "name"+ name+ "salary"+ salary+ "hireDay"+ hireDay;
	}
}
           
package equals_hashCode_toString;

public class Manager extends Employee {
	private double bonus;
	
	public Manager(String name, double salary, int year, int month, int day) {
	     super(name, salary, year, month, day);
	     bonus = 0;
	}
	
	public double getSalary()
	{
		double baseSalary = super.getSalary();
		return baseSalary + bonus;
	}
	
	public void setBonus (double bonus)
	{
		this.bonus = bonus;
	}
	
	public boolean equals(Object otherObject)
	{
		//super.equals 方法检查otherObject除了bonus属性之外是否相等
		if (!super.equals(otherObject))
			return false;
		
		Manager manager = (Manager) otherObject;
		return bonus ==manager.bonus;
	}
	
	public int hashCode() {
		return super.hashCode()+ 17* new Double(bonus).hashCode();
	}
	
	public String  toString() {
		return super.toString()+"bonus"+ bonus;
	}
}
           
package equals_hashCode_toString;

public class EqualsTest {

	public static void main(String[] args) {
		Employee Alice1 = new Employee("Alice", 7500, 2016, 10, 17);
		Employee Alice2 = Alice1;
		Employee Alice3 = new Employee("Alice", 7500, 2016, 10, 17);
		Employee Bob = new Employee("Bob", 7500, 2016, 10, 17);
		
		System.out.println("Alice1 == Alice2:" +(Alice1 == Alice2));
		System.out.println("Alice1 == Alice3:" +(Alice1 == Alice3));
		System.out.println("Alice1.equals(Alice2):" +(Alice1.equals(Alice2)));
		System.out.println("Alice1.equals(Alice3):" +(Alice1.equals(Alice3)));
		
		System.out.println("Alice1.hashCode():"+Alice1.hashCode());
		System.out.println("Alice2.hashCode():"+Alice2.hashCode());
		System.out.println("Alice3.hashCode():"+Alice3.hashCode());
		
		System.out.println("Bob.toString: "+ Bob.toString());
		
		Manager Lily1 = new Manager("Lily", 4000, 2015, 3, 77);
		Manager Lily2 = new Manager("Lily", 4000, 2015, 3, 77);
		Lily1.setBonus(3500);
		System.out.println("Lily1.toString:"+Lily1.toString());
		System.out.println("Lily1.equals(Lily2):"+ Lily1.equals(Lily2));
		
		System.out.println("Lily1.hashCode():"+Lily1.hashCode());
		System.out.println("Lily2.hashCode():"+Lily2.hashCode());
		
	}

}