other.bonus;
测试代码和输出:
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));
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("carl.equals(boss): " + carl.equals(boss));
}
输出为:
alice1 == alice2: true
alice1 == alice3: false
alice1.equals(alice3): true
alice1.equals(bob): false
carl.equals(boss): false
这里有一个问题,就是如果是通过if (getClass() != otherObject.getClass()) return false;来判断,如果两个对象不在一个类中,那么就判断称这两个对象不相等。但是如果两个参数不属于同一个类,该如何处理呢,如果修改成if (!otherObject instanceof Employee) return false;(A instance B的意思是,如果A是B的一个实例或B的子类的一个实例,就返回true,否则返回false)这样的话如果要满足对称性的要求的话会有一些问题,例如,e.equals(m)中的e是一个Employee对象,m是一个Manager对象,并且两个对象具有相同的姓名、薪水和雇佣日期,如果在Employee.equals中调用instanceof检测,则返回true,这没问题。但是如果要反过来的话,如果要让m.equals(e)也返回true,这就要求Manager类的equals方法必须能够用自己与任何一个Employee对象进行比较,然而Manager类的对象还具有Employee类的对象没有的独特的实例域,这就使得使用instanceof出现了问题。
为了解决这个问题,可以从两个方面考虑:(1)如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。(2)如果由父类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。
例如:如果两个Manager对象所对应的姓名、薪水和雇佣日期均相等,但是奖金不相等,就认为它们不相同,因此,可以使用getClass检测。如果使用雇员的ID作为相等检测的标准,并且这个相等的概念适用于所有的子类,就可以使用instanceof检测,并应该将Employee.equals声明为final。
Java语言规范要求equals方法应该具有如下特性:
自反性:对于任何非空引用x,x.equals(x)应该返回true
对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true
传递性:对于任何引用x和y,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)也应该返回ture
一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果
对于任意非空引用x,x.equals(null)应该返回false
一个完美的equals方法应该这样编写:
(1)显示参数命名为otherObject
(2)检测this与otherObject是否引用同一个对象:if(this == otherObject) return true;
(3)检测otherObject
是否为空: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)对所有需要比较的域进行比较,使用“==”比较基本类型域,使用Object.equals方法比较对象域,如果所有的域都匹配,就返回true,否则,返回false
(7)如果需要在子类中重新定义equals,要先调用父类的equals方法super.equals()
2.hashCode方法
散列码(Hash Code)是由对象导出的一个整型值。散列码是没有规律的,如果x和y是两个不同的对象,x.hashCode()与y.hashCode()基本上不会相同。
这里必须强调的是:equals方法和hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()和y.hashCode()必须相等。
在Employee类中使用下面的代码计算对象的hashCode值:为了实现两个对象如果姓名、薪水和雇佣日期都相等那么这两个对象相等,可以按照下面的 方法得到hashCode值,这里是因为相同字符串拥有相同的散列码,因为字符串的散列码是由内容导出的,有相同的计算公式。
public int hashCode()
return Objects.hash(name, salary, hireDay);
Objects.hash方法会对各个参数调用hashCode方法,并组合这些散列值。
子类Manager类中的hashCode方法:
public int hashCode()
return java.util.Objects.hash(super.hashCode(), bonus);
测试代码和输出:
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);
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("alice1.hashCode(): " + alice1.hashCode());
System.out.println("alice3.hashCode(): " + alice3.hashCode());
System.out.println("bob.hashCode(): " + bob.hashCode());
System.out.println("carl.hashCode(): " + carl.hashCode());
alice1.hashCode(): -808853550
alice3.hashCode(): -808853550
bob.hashCode(): -624019882
carl.hashCode(): -2004699436
3.toString方法
Object中的toString方法用于返回表示对象值的字符串。
Employee中的toString方法:
public String toString()
return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay
+ "]";
子类Manager中的toString方法:
public String toString()
return super.toString() + "[bonus=" + bonus + "]";
测试和输出:
public static void main(String[] args)
Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);
System.out.println("bob.toString(): " + bob);
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
boss.setBonus(5000);
System.out.println("boss.toString(): " + boss);
bob.toString(): aaaaTest.Employee[name=Bob Brandson,salary=50000.0,hireDay=1989-10-01]
boss.toString(): aaaaTest.Manager[name=Carl Cracker,salary=80000.0,hireDay=1987-12-15][bonus=5000.0]