equals方法是我们日常编程中很常见的方法,Object中对这个方法的解释如下:

boolean

equals(Object obj) 指示其他某个对象是否与此对象“相等”。

  查看该方法的底层代码如下:

public boolean equals(Object obj) {return (this == obj);}

通过上面的代码很容易就能看出来,Object的equals方法上是用来比较两个实例是否为同一个实例对象,底层还是依赖于==的判断。

2.4.1.1 什么情况下无需覆盖equals方法

Equals方法看起来是个很简单的方法,但是重写equals方法还是很容易出现问题的,并不是说每个类都需要来重写equals方法。满足下面任意条件的类都可以不用覆盖equals方法;

  • l 类的每个实例本质上都是唯一的,如枚举,单例等;
  • l 不关心类是否提供了“逻辑相等”的测试功能。如Random覆盖equals,检查两个Random实例是否产生相同的随机数序列,但是这其实是很不必要的,所以从Object中继承来的equals方法已经足够了。
  • l 超类已经覆盖了equals方法,从超类集成过来的行为对于子类也是合适的。如List实现从ArrayList继承equals实现。
  • l 类是私有的或者包访问权限的,可以确定它的equals方法永远不会被调用。

通过上面的叙述,知道了只要满足四种情况,那么无疑覆盖equals方法,既是多余也容易出现问题,我们应该加以避免。

2.4.1.2 覆盖equals方法

  了解了无需覆盖equals的条件,那么什么条件中覆盖equals方法呢?覆盖equals需要注意什么呢?

  当类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals方法以实现期望的行为时,就需要覆盖equals方法。

  上面通常属于“值类(value class)”的情形。值类仅仅是一个表示值的类,如Integer、Date、String等。程序员在利用equals方法来比较值对象的引用时,希望知道逻辑上的值是否相等,而并非比较是否指向同一个对象。为了满足程序员的要求,覆盖equals是很必须的,这样做也使得这个类的实例可以用做映射表(map)的键(key),或者集合(set)的元素,使映射表或map中表现出预期的行为,如不存放具有相同值。

  但是Java中有一种值类却不在这一要求之中,那就是枚举。在覆盖equals方法时,必须遵循以下的通用约定:

  • l 自反性(reflexive):对于任何非null的引用值x,x.equals(x)必须返回true。
  • l 对称性(symmetric):对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
  • l 传递性(transitive):对于任何非null的引用值x,y和z,如果x.equals(y)返回true,且y.equals(z)返回true,那么x.equals(z)也必须返回true。
  • l 一致性(consistent):对于任何非null的引用值x和y,只有equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)总会一致地返回相同的结果。
  • l 非空性(Non-nullity):对于任何非null的引用值x,x.equals(null)必须返回false。

  这些约定看起来很简单,但是在实际编程中很容易违反这些约定,一旦违反这些约定,程序运行就会不正常,接下来通过例子展示违反约定的情况以及如何避免这些错误。

自反性

  自反性:如果我们书写的equals方法形式的功能不是用于判断自己的对象是否等于自身(或是自身包含的值),那么该会是多么可怕的事情呢?如List中存放实例,然后List判断contains()该对象时,那么就会永远都会返回false;通过代码来验证一下:

package cn.object.override.equals;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class EqualsDemo {@Testpublic void test(){EqualsDemo demo1 = new EqualsDemo();EqualsDemo demo2 = new EqualsDemo();List<EqualsDemo> list = new ArrayList<>();list.add(demo1);System.out.println("list是否包含demo1:"+list.contains(demo1));System.out.println("list是否包含demo2:"+list.contains(demo2));}@Overridepublic boolean equals(Object obj) {// TODO Auto-generated method stubreturn false;}
}

  我们通过ArrayList的底层代码知道contains是通过调用当前类的indexOf方法来确定的,而indexOf是通过循环遍历判断参数和集合中的元素是否相等,而这个判断是通过实例的equals方法来确定的,所以上面的两个判断都是false也就可以理解了,这就是equals不正确覆盖带来的严重后果了;

对称性

  对称性也是很重要的一个要求,并且逻辑思维也应该是这样的,那么如果我们不正常的覆盖,导致情况不是这样的会怎么样呢?通过一个代码来实现;

package cn.object.override.equals;
import org.junit.Test;
/*** @author YorickYou* Code Address:https://github.com/YorickYou/JavaSE.git*/
public class CaseInsensitiveString {private final String s;//有参构造public CaseInsensitiveString(String s) {if (s == null)throw new NullPointerException();this.s = s;}@Overridepublic boolean equals(Object obj) {if(obj instanceof CaseInsensitiveString)return s.equalsIgnoreCase(((CaseInsensitiveString) obj).s);if (obj instanceof String) return s.equalsIgnoreCase((String)obj);return false;}public static void main(String[] args) {CaseInsensitiveString string1 = new CaseInsensitiveString("abc");String str = "abc";//两次调用equals方法,前者是调用的CaseInsensitiveString的equals方法,后者是String的equals方法System.out.println(string1.equals(str)+"自反性"+str.equals(string1));}
}

  上面对equals方法的覆盖就严重违反了对称性.如果我们往集合中添加该对象,那么此时会出现什么情况呢?

public static void test1(){List<CaseInsensitiveString> list = new ArrayList<CaseInsensitiveString>();CaseInsensitiveString string1 = new CaseInsensitiveString("abc");String str = "abc";list.add(string1);System.out.println(string1.equals(str)+"--"+list.contains(str)+"--"+list.contains(string1));}//true--false--true

所以在覆盖equals方法时,我们一定要注意对称性,对于上面问题的解决只需要将String的互操作这一段从equals中移除即可.

package cn.object.override.equals;import java.util.ArrayList;
import java.util.List;import org.junit.Test;/*** * @author YorickYou* Code Address:https://github.com/YorickYou/JavaSE.git*/
public class CaseInsensitiveString {private final String s;//有参构造public CaseInsensitiveString(String s) {if (s == null)throw new NullPointerException();this.s = s;}    public static void main(String[] args) {CaseInsensitiveString string1 = new CaseInsensitiveString("abc");String str = "abc";//两次调用equals方法,前者是调用的CaseInsensitiveString的equals方法,后者是String的equals方法System.out.println(string1.equals(str)+"自反性"+str.equals(string1));test1();}@Overridepublic boolean equals(Object obj) {// TODO Auto-generated method stubreturn obj instanceof CaseInsensitiveString && ((CaseInsensitiveString)obj).s.equalsIgnoreCase(s);}public static void test1(){List<CaseInsensitiveString> list = new ArrayList<CaseInsensitiveString>();CaseInsensitiveString string1 = new CaseInsensitiveString("abc");String str = "abc";list.add(string1);System.out.println(string1.equals(str)+"--"+list.contains(str)+"--"+list.contains(string1));}
}

转载于:https://www.cnblogs.com/lin-jing/p/8315171.html

重写equals方法(未完)相关推荐

  1. hashcode相等的两个对象一定相等吗_为什么重写 equals方法时一定要重写hashCode方法?...

    推荐阅读: 一线架构师总结SpringBoot,Cloud,Nginx与Docker,不信你搞不懂 47天洒热血复习,我终于"挤进"了字节跳动(附面经+学习笔记) 五年时间,从蘑菇 ...

  2. 重写equals方法原则

    写equals方法的原则是什么 看官方文档给出的解释 It is reflexive: for any non-null reference value x, x.equals(x) should r ...

  3. 面试题:重写equals方法为什么通常会重写hashcode方法?

    最近在面试的时候,当问完了HashMap的数据结构之后,通常会再多问一个问题,就是:重写equals方法时通常为什么也要重写一下hashcode方法? 其实这个问题,本质上又回到HashMap的应用场 ...

  4. 为什么重写equals方法时一定要重写hashCode方法

    在每个类中,在重写equals方法的时侯,一定要重写hashcode方法. 根据Object规范,规范约定: 如果两个对象通过equals方法比较是相等的,那么它们的hashCode方法结果值也是相等 ...

  5. JAVA中重写equals()方法的同时要重写hashcode()方法

    object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true:注意:当此方法 ...

  6. 重写 equals 方法就一定要重写 hashCode 方法?其实有个前提

    作者 l 会点代码的大叔(CodeDaShu) 如果问到 == 和 equals 的区别,相信很多程序员同学都能脱口而出:一个是判断地址,一个是判断内容. 但是如果继续追问:"你重写过 eq ...

  7. 重写equals方法的hashcode_Java equals 和 hashCode 的这几个问题可以说明白吗?

    前言 上一篇文章 如何妙用Spring 数据绑定机制,灵魂追问 环节留下了一个有关 equals 和 hashcode 问题 .基础面试经常会碰到与之相关的问题,这不是一个复杂的问题,但很多朋友都苦于 ...

  8. 重写equals方法时必须重写hashcode方法吗

    重写equals方法时必须重写hashcode 有规范: 1,当obj1.equals(obj2) 为 true 时,obj1.hashCode() == obj2.hashCode() 2,当obj ...

  9. Java中重写equals()方法时注意点

    Java中重写equals()方法时注意点 一直说,重写一个对象的equals()方法时我们必须重写HashCode()方法,但是如果我们不重写呢?会有什么影响呢? 首先看一下,什么情况下我们需要重写 ...

最新文章

  1. PyTorch基础与简单应用:构建卷积神经网络实现MNIST手写数字分类
  2. JqueryMobile链接一个页面,而链接页面中图片需刷新才显示的问题
  3. SAP UI5 Tools 里配置文件 ui5-local.yaml 的配置要点
  4. c语言实现二分法_C语言实现二分法求解方程在区间内的根
  5. 考研 | 先预览一下考研真题是考研小白最快的入门方法(含21考研最新真题)
  6. Spring框架学习笔记01:初探Spring——采用Spring配置文件管理Bean
  7. centos linux asp,CentOS 7.4 下 如何部署 AspNetCore 结合 consul
  8. 直接复制php的安装目录部署到其他服务器的时候,无法运行
  9. 核电安全级数字化仪控系统内存诊断设计与实现
  10. TCP BBR - 一键安装最新内核并开启 TCP BBR
  11. python大漠游戏多开_python游戏脚本多开天才生成器
  12. c语言else的用法,else的用法
  13. 子在川上曰:nginx的安装和配置、node服务器的配置、mongdb的安装、pm2进行项目动态管理
  14. 数据库三级考试 真题存储过程汇总(含答案)
  15. 关于阿里云ACP认证的那些事儿
  16. Python 人工智能入门须知
  17. 用c语言实现作曲与播放教程~,原创哦~
  18. 所有学习资源都给你你汇总好啦!
  19. 机器人中的坐标转换关系(个人记录学习)
  20. 第六章 相机及其应用 6.3欧拉角、旋转矩形、四元数、应用于Eigen的示例

热门文章

  1. pyqt5 qstring在哪个库_PyQt模型/视图结构编程示例:QStringListModel的用法
  2. ORACLE中exists与in的区别
  3. 阿里云ECS利用密钥对ssh登录服务器
  4. BIM 360 Docs API在操作欧洲数据中心内容的一些调整
  5. JS调用模式以及bind()方法
  6. 《计算机系统:系统架构与操作系统的高度集成》——3.2 处理器实现涉及什么...
  7. Union/find--不相交集类(并查集)
  8. SQL Server 将在下一个版本实现内存中运行
  9. [Android实例] 天天动听 悬浮歌词(迷你歌词)效果解读
  10. Java插件项目不能转了问题