文章目录
1、概述
Object类是Java中所有类的超类,可以使用Object类型的变量来引用任何类型的对象。在Java中,只有基本类型不是对象,除此之外,所有的数组类型,不管是对象数组还是基本类型数组,都扩展了Object类。
2、equals方法
Object类中的equals方法用于检测一个对象是否等于另一个对象,这个方法将判断两个对象状态的相等性。由于任何类都继承自Object,而Object类中的equals方法仅仅用来判断两个对象是否具有相同的引用,所以在自定义类中,需要自己实现相应的equals方法,并在方法开始处调用超类的equals方法。
2.1 equals方法需要具备的特性
- 自反性:对于任何非空引用x,x.equals(x)应该返回true
- 对称性:对于任何非空引用x和y,x.equals(y)和y.equals(x)应该返回相同结果
- 传递性:对于任何非空引用x,y和z,x.equals(y)返回true且y.equals(z)返回true,则x.equals(z)也应该返回true
- 一致性:如果x和y引用的对象没有发生变化,返回调用x.equals(y)应该返回同样的结果
- 对于任何非空引用x,x.equals(null)应该返回false
2.2 equals方法中如何判断两个对象是否属于同一个类
若使用instanceof关键字,则很容易会违反对称性。假设Manager类的超类是Employee类,Employee类中的equals方法这样定义到:
if((!otherObject instanceof Employee))
return false;
假设对象Employee e和Manager m具有相同属性,调用e.equals(m)返回了true,因为Manager instanceof Employee 为true。但是调用m.equals(e)却返回了false,因为Employee instanceof Manager为false。这就违反了对称性。
实际上,这两个对象所属的类虽然有继承关系,但是不应该相等。所以在判断类是否相等,应该使用getClass来判断:
if(e.getClass() != m.getClass())
return false;
2.3 编写equals方法的建议
(1)显示参数命名为otherObject,在比较域值时,将其转换为对应类other变量;
(2)先检测是否具有相同引用
if(this == otherObject) return true;
(3)检测otherObject是否为null
if(otherObject == null) return false;
(4)比较this与otherObject是否属于同一类
if(getClass() != other.getClass()) return false;
当然,如果所有子类的equal具有同一语义,就应该使用instanceof来比较,这时候对于继承关系的类的对象,如果它们所有域值相同,认为它们也相同。
(5)将otherObject转换为相应类型变量,比较其基本域
ClassName other = (ClassName) otherObject
return xxx == xxx && xxxx == xxxx && ...
3 hasCode方法
hasCode方法返回一个整型值散列码,散列码没有规律,但是两个不同的对象应该具有不同的散列码(非严格)。
3.1 hasCode与equals的关系
如果两个对象equal,那么其散列码一定要相同;反之如果两个对象不equal,不一定要求其散列码不相同,但通常是不相同的。
产生不同的散列码有助于优化哈希表性能,尽量避免哈希冲突,但是真正发生了哈希冲突,可以通过链式地址法,在同一个哈希地址建立一个链表将具有同样哈希地址的内容链接。
这就解释了散列码相同的对象也有可能是不equal的。
3.2 自定义hasCode函数
如果自定义了类却没有重写hasCode方法,默认调用Object中的hasCode方法,它返回的是对象的引用地址。
但是在重写equals方法和hasCode方法,必须确保其满足3.1中的条件。
4 toString方法
一般建议为自定义类都重写一个toString()方法,一般遵循格式:类名+[field1=xx,filed2=xx,…]。
5 实例
Java核心技术卷1第10版 程序清单5-8 5-9 5-10
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name,double salary,int year,int month,int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year,month,day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent;
salary += raise;
}
public boolean equals(Object otherObject)
{
//两个对象引用相同,则对象一定相同
if (this == otherObject)
return true;
//比较对象为空,则对象一定不相同
if (otherObject == null)
return false;
//若对象的类不同,则对象一定不同
if (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 hasCode()
{
return Objects.hash(name,salary,hireDay);
}
public String toString()
{
return getClass().getName() + "[name=" + name + ",salary=" + salary
+ ",hireDay=" + hireDay + "]";
}
}
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 void setBonus(double bonus)
{
this.bonus = bonus;
}
public double getBonus()
{
return bonus;
}
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject))
return false;
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
public int hasCode()
{
return super.hasCode() + 17 * new Double(bonus).hashCode();
}
public String toString()
{
return super.toString() + "[bonus=" + bonus + "]";
}
}
public class EqualsTest {
public static void main(String[] args)
{
Employee alice1 = new Employee("Alice Adams",75000,1987,12,15);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alice Adams",75000,1987,12,15);
Employee bob = new Employee("Bob Brandson",50000,1989,10,1);
System.out.println("alice1 == alice2: " + (alice1 == alice2));
System.out.println("alice1 == alice3: " + (alice1 == alice3));
System.out.println("alice1.equals(alice3): " + (alice1.equals(alice3)));
System.out.println("alice1.equals(bob): " + (alice1.equals(bob)));
System.out.println("bob.toString(): " + bob);
Manager carl = new Manager("Carl Cracker",80000,1987,12,15);
Manager boss = new Manager("Carl Cracker",80000,1987,12,15);
boss.setBonus(5000);
System.out.println("boss.toString(): " + boss);
System.out.println("carl.equals(boss): " + carl.equals(boss));
System.out.println("alice1.hasCode(): " + alice1.hasCode());
System.out.println("alice3.hasCode(): " + alice3.hasCode());
System.out.println("bob.hasCode(): " + bob.hasCode());
System.out.println("carl.hasCode(): " + carl.hasCode());
System.out.println("boss.hasCode(): " + boss.hasCode());
}
}
结果
alice1 == alice2: true
alice1 == alice3: false
alice1.equals(alice3): true
alice1.equals(bob): false
bob.toString(): equals.Employee[name=Bob Brandson,salary=50000.0,hireDay=1989-10-01]
boss.toString(): equals.Manager[name=Carl Cracker,salary=80000.0,hireDay=1987-12-15][bonus=5000.0]
carl.equals(boss): false
alice1.hasCode(): -808853550
alice3.hasCode(): -808853550
bob.hasCode(): -624019882
carl.hasCode(): -341762419
boss.hasCode(): 931997325
Process finished with exit code 0