Java相等性测试
2015-08-04 18:09
369 查看
最近在看《Java核心技术卷I》时有看到相等性测试的相关内容,就一些学习心得做一下分享。
1.Object类中的equals方法
Object类是java中所有类的始祖,Object类型的变量可以引用任何类型的对象。java中只有基本类型不是对象,所有的数组类型不管是对象数组还是基本类型数组都扩展于Object类。Object类中的equals方法用于判断两个对象是否具有相同的引用,如果两个对象具有相同的引用,它们一定是相等的。但对于多数类来说,这种判断没有什么意义。大多数时候需要检测的是对象的状态是否相等。
2.重写equals方法
以下实现了重写equals方法的Employee类和Manager类。
子类中定义equals方法时,首先调用超累的equals。如果超类检测失败,对象就不可能相等。如果超类中的域相等则需要比较子类中的实例域。
3.相等性测试特点
(1)自反性:对于任何非空引用x,x.equals(x)应该返回true.
(2)对称性:对于任何引用x和y,x.equals(y)和y.equals(x)都应该返回true.
(3)传递性:对于任何引用x/yz,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)也应该返回true.
(4)一致性:如果x、y没有发生变化,反复调用x.equals(y)应返回同样结果.
(5)对于任意非空引用x,x.equals(null)应返回false.
(6)如果x.equals(y)返回true,则必须有x.hashCode()==y.hashCode().
4.继承中的相等性测试
就对称性来说,当参数不属于同一个类的时候需要十分注意。比如:
在继承关系中的相等性测试,应该根据语义分两种情况考虑:
(1)如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。
(2)如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。
对于(1)的情况:比如两个对象所对应的名字,薪水和雇佣日期相等而奖金不相等,则认为它们是不同的。
对于(2)的情况,假设使用name作为相等的检测标准,并且这个相等的概念适用于所有子类,就可以使用instanceof进行检测,并且应该将超类的equals方法声明为final。
5.重写完美的equals
(1)显示参数参数命名为otherObject,稍后需要将它转换成另一个other的变量;
(2)检测this与otherObject是否引用同一个对象;
(3)检测otherObject是否为null;
(4)比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:
(6)比较实例域,使用==比较基本类型域,使用equals比较对象域.
(7)如果在子类中重新定义equals,首先测试超类的相等性.
1.Object类中的equals方法
Object类是java中所有类的始祖,Object类型的变量可以引用任何类型的对象。java中只有基本类型不是对象,所有的数组类型不管是对象数组还是基本类型数组都扩展于Object类。Object类中的equals方法用于判断两个对象是否具有相同的引用,如果两个对象具有相同的引用,它们一定是相等的。但对于多数类来说,这种判断没有什么意义。大多数时候需要检测的是对象的状态是否相等。
2.重写equals方法
以下实现了重写equals方法的Employee类和Manager类。
class Employee { private static int nextId = 1; private String name; private double salary; private int id; private Date hireDay; public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; id = 0; GregorianCalendar calendar = new GregorianCalendar(year, month-1 ,day); hireDay = calendar.getTime(); } public boolean equals(Object otherObject) { //a quick test to see if the objects are identify if (this == otherObject) return true; //must return false if the explicit parameter is null if (otherObject == null) return false; //if the classes don't match, they can't be equal if (getClass() != otherObject.getClass()) return false; //now we know otherObject is a non-null Employee Employee other = (Employee) otherObject; //test whether the fields have identical values return Objects.equals(name,other.name) && salary == other.salary && Objects.equals(hireDay,other.hireDay); } }
</pre><span style="white-space:pre"> </span>注意Employee.equals方法的最后一条语句,如果改为 :<p></p><p></p><pre name="code" class="cpp">return name.equals(other.name) && salary == other.salary && hireDay.equals(other.hireDay);则如果name或者hireDay为null会报异常。
子类中定义equals方法时,首先调用超累的equals。如果超类检测失败,对象就不可能相等。如果超类中的域相等则需要比较子类中的实例域。
class Manager extends Employee { private double bonus; public Manager(String n, double s, int year, int month, int day) { super(n,s,year,month,day); bonus = 0; } public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } public void setBonus(double b) { bonus = b; } public boolean equals(Object otherObject) { if(!super.equals(otherObject)) return false; Manager other = (Manager) otherObject; //super.equals checked that this and other belong to the same class return bonus == other.bonus; } }
3.相等性测试特点
(1)自反性:对于任何非空引用x,x.equals(x)应该返回true.
(2)对称性:对于任何引用x和y,x.equals(y)和y.equals(x)都应该返回true.
(3)传递性:对于任何引用x/yz,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)也应该返回true.
(4)一致性:如果x、y没有发生变化,反复调用x.equals(y)应返回同样结果.
(5)对于任意非空引用x,x.equals(null)应返回false.
(6)如果x.equals(y)返回true,则必须有x.hashCode()==y.hashCode().
4.继承中的相等性测试
就对称性来说,当参数不属于同一个类的时候需要十分注意。比如:
Employee e = new Employee("a",100,2000,10,10); Manager m = new Manager("a",100,2000,10,10);当Employee类的equals方法中的第三个判断使用intanceof进行检测,则e.equals(m)返回true。而m.equals(e)返回false,不满足对称性。这是因为:
if (m instanceof Employee) System.out.println("子类对象是父类的实例");//子类是父类的实例 if (!(e instanceof Manager)) System.out.println("父类对象不是子类的实例");//父类对象不是子类的实例如果使用getClass()来进行检测:
if (e.getClass() != m.getClass()) System.out.println(" 超类.getClass() != 子类.getClass()"); //超类.getClass() != 子类.getClass()此时e.equals(m)和m.equals(e)都返回false,满足对称性。此时只要是子类与超类测试相等性则都会返回false。
在继承关系中的相等性测试,应该根据语义分两种情况考虑:
(1)如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。
(2)如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。
对于(1)的情况:比如两个对象所对应的名字,薪水和雇佣日期相等而奖金不相等,则认为它们是不同的。
对于(2)的情况,假设使用name作为相等的检测标准,并且这个相等的概念适用于所有子类,就可以使用instanceof进行检测,并且应该将超类的equals方法声明为final。
5.重写完美的equals
(1)显示参数参数命名为otherObject,稍后需要将它转换成另一个other的变量;
(2)检测this与otherObject是否引用同一个对象;
(3)检测otherObject是否为null;
(4)比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:
if(getClass() != otherObject.getClass()) return false;如果所有子类都拥有统一的语义,就使用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;(5)将otherObject转换为相应的类类型变量(this类类型)
(6)比较实例域,使用==比较基本类型域,使用equals比较对象域.
(7)如果在子类中重新定义equals,首先测试超类的相等性.
public class StaticTest { public static void main(String[] args) { Employee a = new Employee("a",100,2000,10,10); Employee b = a; Employee c = new Employee("a",100,2000,10,10); Employee d = new Employee("d",200,2001,5,5); System.out.println("a == b:" + (a == b));//true System.out.println("a == c:" + (a == c));//false,"=="对比栈上的对象变量 System.out.println("a.equals(c):" + (a.equals(c)));//true,equals()方法对比堆中的对象成员 System.out.println("a.equals(d):" + a.equals(d));//false System.out.println("d.toString():" + d);//只要对象与一个字符串通过操作符"+"连接起来,java编译器就会自动调用toString(); Manager carl = new Manager("Carl",800,1990,12,15); Manager boss = new Manager("Carl",800,1990,12,15); boss.setBonus(500); System.out.println("boss.toString():" + boss); System.out.println("carl.equals(boss):" + carl.equals(boss)); if (carl instanceof Employee) System.out.println("子类对象是父类的实例");//子类是父类的实例 if (!(a instanceof Manager)) System.out.println("父类对象不是子类的实例");//子类是父类的实例 } }
相关文章推荐
- Java实现文件复制
- 使用spring的aop进行权限拦截
- Maven+SpringMVC+MyBatis 上传图片
- 【Java并发编程实战】—–synchronized
- java字符串的hashCode
- springmvc运行机制
- Java基础---折半查找和进制转换
- 【转帖】ECLIPSE-JEE-LUNA-SR2官方汉化教程
- Java中正则表达式的使用
- spring线程池ThreadPoolExecutor配置
- 代理模式与Spring
- Java里到底是引用传递还是值传递
- Spring mvc 用FreeMarker模板化
- new Thread的弊端及Java四种线程池的使用
- Java线程池使用说明
- Java中this关键字的几种用法
- maven中使用spring的test包结合junit4进行测试。
- MyBatis Spring整合配置映射接口类与映射xml文件
- Java并发编程之Callable,Future,FutureTask
- Java一处编译到处运行的简单原理