Java中判断对象是否相等的equals()方法使用教程

 更新时间:2016年05月26日 08:59:21   作者:xiongchao2011  
与==运算符响应,equals()方法也是Java中对对象进行比较的一大方式,要特别注意二者的不同点,这个我们在下文中即将讲到,接下来我们就来看一下Java中判断对象是否相等的equals()方法使用教程

Object类中的equals方法用于检测一个对象是否等于另一个对象。在Object类中,这个方法判断两个对象是否具有相同的引用,如果两个对象具有相同的引用,它们一定是相等的。从这点上看,将其作为默认操作也是合乎情理的。然而,对于多数类类说,这种判断并没有什么意义,例如,采用这种方式比较两个PrintStream是否相等就完全没有意义。然而,经常需要检测两个对象状态的相等性,如果两个对象的状态相等,就认为这两个对象是相等的。所以一般在自定义类中都要重写equals比较。

下面给出编写一个完美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是否属于同一个类,如果equals的语义在每个子类中有所改变,就使用getClass()检测,它将自己作为目标类

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

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

if(!(otherObject instanceof ClassName)) return false;

(5)将otherObject转换为相应类型的变量:

ClassName other=(ClassName)otherObject;

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

return field1==other.field1&&field2.equals(other.field2)

如果在子类中重新定义equals,就要在其中包含调用super.equals(other)。如果检测失败,就不可能相等。如果超类中的域相等,就比较子类中的实例域。

对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的元素是否相等。

来看几个字符串比较例子:

String a = "abc"; 
String b = "abc"; 
String c = new String("abc"); 
String d = new String("abc"); 
System.out.println(a == b); // true 因为JAVA中字符串常量是共享的,只有一个拷贝 
System.out.println(a == c); // false a和c属于2个不同的对象 
System.out.println(a.equals(c)); // true 由于String对象的equals方法比较的是对象中的值,所以返回true。(和Object的equals方法不同) 
System.out.println(c==d); // false c和d虽然对象内的值相同,但属于2个不同的对象,所以不相等 
System.out.println(c.equals(d)); // true 

简单的说,当比较字符串常量时,等于和equals返回的结果一样,当想比较字符串对象的值时用equals。

看一个equals的使用例子:

package chapter05.EqualsTest; 
 
import java.util.*; 
 
public class EqualsTest { 
 public static void main(String[] args) { 
  Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15); 
  Employee alice2 = alice1; // reference the same object 
  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()); 
 } 
} 
 
class Employee { 
 public Employee(String n, double s, int year, int month, int day) { 
  name = n; 
  salary = s; 
  GregorianCalendar calendar = new GregorianCalendar(year, month, day); 
  hireDay = calendar.getTime(); 
 } 
 
 public String getName() { 
  return name; 
 } 
 
 public double getSalary() { 
  return salary; 
 } 
 
 public Date getHireDay() { 
  return hireDay; 
 } 
 
 public void raiseSalary(double byPercent) { 
  double raise = salary * byPercent / 100; 
  salary += raise; 
 } 
 
 @Override 
 public boolean equals(Object otherObject) { 
  // a quick test to see if the objects are identical 
  if (this == otherObject) 
   return true; 
 
  // must return false if the explicit parameter is null 
  if (otherObject == null) 
   return false; 
 
  // if the classed 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 hava identical values 
  return name.equals(other.name) && salary == other.salary 
    && hireDay.equals(other.hireDay); 
 
 } 
 
 @Override 
 public int hashCode() { 
  return 7 * name.hashCode() + 11 * new Double(salary).hashCode() + 13 
    * hireDay.hashCode(); 
 } 
 
 @Override 
 public String toString() { 
  return getClass().getName() + "[name=" + name + ",salary=" + salary 
    + ",hireDay=" + hireDay + "]"; 
 } 
 
 private String name; 
 private double salary; 
 private Date hireDay; 
} 
 
class Manager extends Employee { 
 public Manager(String n, double s, int year, int month, int day) { 
  super(n, s, year, month, day); 
  bouns = 0; 
 } 
 
 @Override 
 public double getSalary() { 
  double baseSalary = super.getSalary(); 
  return baseSalary + bouns; 
 } 
 
 public void setBouns(double b) { 
  bouns = b; 
 } 
 
 @Override 
 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 bouns == other.bouns; 
 } 
 
 @Override 
 public int hashCode() { 
  return super.hashCode() + 17 * new Double(bouns).hashCode(); 
 } 
 
 @Override 
 public String toString() { 
  return super.toString() + "[bouns=" + bouns + "]"; 
 } 
 
 private double bouns; 
} 

深入
下面根据“类是否覆盖equals()方法”,将它分为2类。
(1) 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。
(2) 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。
下面,举例对上面的2种情况进行说明。
1. “没有覆盖equals()方法”的情况
代码如下 (EqualsTest1.java):

import java.util.*;
import java.lang.Comparable;

/**
 * @desc equals()的测试程序。

 */
public class EqualsTest1{

 public static void main(String[] args) {
  // 新建2个相同内容的Person对象,
  // 再用equals比较它们是否相等
  Person p1 = new Person("eee", 100);
  Person p2 = new Person("eee", 100);
  System.out.printf("%s\n", p1.equals(p2));
 }

 /**
  * @desc Person类。
  */
 private static class Person {
  int age;
  String name;

  public Person(String name, int age) {
   this.name = name;
   this.age = age;
  }

  public String toString() {
   return name + " - " +age;
  }
 }
}

运行结果:

复制代码 代码如下:
false

结果分析
我们通过 p1.equals(p2) 来“比较p1和p2是否相等时”。实际上,调用的Object.java的equals()方法,即调用的 (p1==p2) 。它是比较“p1和p2是否是同一个对象”。
而由 p1 和 p2 的定义可知,它们虽然内容相同;但它们是两个不同的对象!因此,返回结果是false。

2. "覆盖equals()方法"的情况
我们修改上面的EqualsTest1.java:覆盖equals()方法。
代码如下 (EqualsTest2.java):

import java.util.*;
import java.lang.Comparable;

/**
 * @desc equals()的测试程序。
 */
public class EqualsTest2{

 public static void main(String[] args) {
  // 新建2个相同内容的Person对象,
  // 再用equals比较它们是否相等
  Person p1 = new Person("eee", 100);
  Person p2 = new Person("eee", 100);
  System.out.printf("%s\n", p1.equals(p2));
 }

 /**
  * @desc Person类。
  */
 private static class Person {
  int age;
  String name;

  public Person(String name, int age) {
   this.name = name;
   this.age = age;
  }

  public String toString() {
   return name + " - " +age;
  }

  /** 
   * @desc 覆盖equals方法 
   */ 
  @Override
  public boolean equals(Object obj){ 
   if(obj == null){ 
    return false; 
   } 

   //如果是同一个对象返回true,反之返回false 
   if(this == obj){ 
    return true; 
   } 

   //判断是否类型相同 
   if(this.getClass() != obj.getClass()){ 
    return false; 
   } 

   Person person = (Person)obj; 
   return name.equals(person.name) && age==person.age; 
  } 
 }
}

运行结果:

复制代码 代码如下:
true

结果分析:
我们在EqualsTest2.java 中重写了Person的equals()函数:当两个Person对象的 name 和 age 都相等,则返回true。
因此,运行结果返回true。
讲到这里,顺便说一下java对equals()的要求。有以下几点:
对称性:如果x.equals(y)返回是"true",那么y.equals(x)也应该返回是"true"。
反射性:x.equals(x)必须返回是"true"。
类推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true"。
一致性:如果x.equals(y)返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是"true"。
非空性,x.equals(null),永远返回是"false";x.equals(和x不同类型的对象)永远返回是"false"。
现在,再回顾一下equals()的作用:判断两个对象是否相等。当我们重写equals()的时候,可千万不好将它的作用给改变了!


equals() 与 == 的区别是什么?
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况(前面第1部分已详细介绍过):
     情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
     情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
下面,通过示例比较它们的区别。
代码如下:

import java.util.*;
import java.lang.Comparable;

/**
 * @desc equals()的测试程序。
 */
public class EqualsTest3{

 public static void main(String[] args) {
  // 新建2个相同内容的Person对象,
  // 再用equals比较它们是否相等
  Person p1 = new Person("eee", 100);
  Person p2 = new Person("eee", 100);
  System.out.printf("p1.equals(p2) : %s\n", p1.equals(p2));
  System.out.printf("p1==p2 : %s\n", p1==p2);
 }

 /**
  * @desc Person类。
  */
 private static class Person {
  int age;
  String name;

  public Person(String name, int age) {
   this.name = name;
   this.age = age;
  }

  public String toString() {
   return name + " - " +age;
  }

  /** 
   * @desc 覆盖equals方法 
   */ 
  @Override
  public boolean equals(Object obj){ 
   if(obj == null){ 
    return false; 
   } 

   //如果是同一个对象返回true,反之返回false 
   if(this == obj){ 
    return true; 
   } 

   //判断是否类型相同 
   if(this.getClass() != obj.getClass()){ 
    return false; 
   } 

   Person person = (Person)obj; 
   return name.equals(person.name) && age==person.age; 
  } 
 }
}

运行结果:

p1.equals(p2) : true
p1==p2 : false

结果分析:
在EqualsTest3.java 中:
(1) p1.equals(p2)
这是判断p1和p2的内容是否相等。因为Person覆盖equals()方法,而这个equals()是用来判断p1和p2的内容是否相等,恰恰p1和p2的内容又相等;因此,返回true。
(2) p1==p2
这是判断p1和p2是否是同一个对象。由于它们是各自新建的两个Person对象;因此,返回false。

相关文章

  • Java中的对象和引用详解

    Java中的对象和引用详解

    这篇文章主要介绍了Java中的对象和引用详解的相关资料,需要的朋友可以参考下
    2017-05-05
  • MyBatis关于二级缓存问题

    MyBatis关于二级缓存问题

    本篇文章主要介绍了MyBatis关于二级缓存问题,二级缓存是Mapper级别的缓存,多个sqlSession操作同一个Mapper,其二级缓存是可以共享的。
    2017-03-03
  • struts2开发流程及详细配置

    struts2开发流程及详细配置

    这篇文章主要介绍了struts2开发流程及详细配置,步骤比较详细,具有一定参考价值,需要的朋友可以了解下。
    2017-09-09
  • Spring IOC推导与DI构造器注入超详细讲解

    Spring IOC推导与DI构造器注入超详细讲解

    这篇文章主要介绍了Spring IOC推导与DI构造器注入,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • 详解java中finalize的实现与相应的执行过程

    详解java中finalize的实现与相应的执行过程

    在常规的java书籍中,即会描述 object的finalize方法是用于一些特殊的对象在回收之前再做一些扫尾的工作,但是并没有说明此是如何实现的.本篇从java的角度(不涉及jvm以及c++),有需要的朋友们可以参考借鉴。
    2016-09-09
  • 基于Java实现简单的时序数据压缩算法

    基于Java实现简单的时序数据压缩算法

    这篇文章主要为大家详细介绍了如何利用Java语言实现简单易懂的时序数据压缩算法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-06-06
  • Java的this关键字的使用与方法的重载相关知识

    Java的this关键字的使用与方法的重载相关知识

    这篇文章主要介绍了Java的this关键字的使用与方法的重载相关知识,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • 数据同步利器DataX简介及如何使用

    数据同步利器DataX简介及如何使用

    DataX 是阿里云 DataWorks数据集成 的开源版本,使用Java 语言编写,在阿里巴巴集团内被广泛使用的离线数据同步工具/平台,今天给大家分享一个阿里开源的数据同步工具DataX,在Github拥有14.8k的star,非常受欢迎
    2024-02-02
  • java ArrayList和Vector的区别详解

    java ArrayList和Vector的区别详解

    这篇文章主要介绍了java ArrayList和Vector的区别详解的相关资料,并附简单实例代码,需要的朋友可以参考下
    2016-11-11
  • 简述Java编程语言中的逃逸分析

    简述Java编程语言中的逃逸分析

    这篇文章主要介绍了简述Java编程语言中的逃逸分析,包括其定义、作用、类型及理论基础等相关内容,十分具有参考价值,需要的朋友可以了解下。
    2017-09-09

最新评论