【0】README

0.1)本文转自 core java volume 1, 旨在理清 equals + hashCode方法;
0.2) 特别说明: 在java中, 只有基本类型不是对象, 例如,数值, 字符和布尔类型的值都不是对象; 但是所有的数组类型,不管是对象数组还是基本类型的数组都扩展于Object类;
0.3) 最后,我们还补充了对 toString 方法的描述;


【1】equals方法

1.1) Object中的 equals 方法用于检测一个对象是否等于另外一个对象;(在Object类中, 这个方法比较的是内存地址, 判断的是两个对象是否具有相同引用)
1.2)看个荔枝:

Hint)

  • H1)为了防备name 或 hireDay 可能为null的情况: 需要使用 Objects.equals 方法;如果两个参数都为 null, Objects.equals(a, b) 返回 true; 如果其中一个为null, 则返回 false; 否则,两个参数都不为null, 则调用 a.equals(b);(注意是Objects not Object)
  • H2)利用这个方法, Employee.equals 方法的最后一条语句要改写为:
return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);

1.3)在子类中定义 equals 方法时, 首先调用超类的 equals; 如果检测失败,对象就不可能相等, 如果超类中的域都相等, 就需要比较子类中的实例域;

1.4)java语言规范要求 equals 具有以下特性:

  • 自反性:非空引用 x,x.equals(x) 应该返回true;
  • 对称性:非空引用x 和 y, x.equals(y) 返回ture, 那么 y.equals(x) 也应该返回ture;
  • 传递性:对于 非空引用x 、y 和 z, x.equals(y) 返回ture, 那么 y.equals(z) 也应该返回ture, 则 x.equals(z) 也应该返回 true;
  • 一致性:如果x 和 y 引用的对象没有发生变化, 反复调用 x.equals(y) 应该返回同样的结果;
  • 对于任意非空应用x, x.equals(null) 应该返回false;

1.5)出现的问题+解决方法

  • 当子类和父类对象分别作为equals 的隐式参数,导致不满足对称性的情况: e.equals(m), 这里的e是一个 Employee对象,m是一个 Manager对象,并且两个对象具有相同的属性值;如果在 Employee.equals中用 instanceof 检测,就会返回 true, 然而这意味着反过来调用: m.equals(e) 也需要返回true; 对称性不允许这个方法调用返回 false, 或者抛出异常;猛然间,让人感觉到 instanceof 并不是那么完美;
  • 下面从两个截然不同的情况看一下这个问题:
    • 1)如果子类能够拥有自己的相等概念, 则对称性需要将强制采用getClass 进行检测;
    • 2)如果由超类决定相等的概念, 那么就可以使用 instanceof 进行检测, 这样可以在不同子类的对象间进行相等的比较;

【2】下面给出编写一个完美的 equals 方法的建议:

  • 2.1)显式参数命名为 otherObject, 稍后需要将它转换为另一个叫做 other的变量;
  • 2.2)检测this 与 otherObject 是否引用同一个对象:
    if(this == otherObject) return true; 实际上, 这是一种经常采用的形式, 因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多;
  • 2.3)检测otherObject 是否为 null, 如果为 null ,则返回 false, 这项检测很有必要:
    if(otherObject == null) return false;
  • 2.4)比较this 与 otherObject 是否属于同一个类: 如果equals 的语义在每个子类中有所改变,就是用 getClass 进行检测:if(getClass() != otherObject.getClass()) return false;
  • 2.5) 将 otherObject 转换为 相应的类类型变量:
    ClassName other = (ClassName)otherObject;
  • 2.6)现在开始对所有需要比较的域进行比较了:
    使用 == 比较基本类型域, 使用 equals比较对象域, 如果所有的域都匹配返回 true, 否则返回 false;
    return field1 == other.field1 && Objects.equals(field2, other.field2) && ……;
    如果在子类中重新定义 equals, 就要在其中包含 调用 super.equals(other);

Hint)对于数组类型的域, 可以使用静态的Arrays.equals 方法检测相应的 数组元素是否相等;
Alert)看个荔枝(下面是实现 equals 方法的一种常见的错误):

代码分析(Analysis):

  • A1)这个方法声明的显式参数类型是 Employee, 其结果并没有 覆盖 Object类的 equals 方法,而是定义了一个完全无关的方法;
  • A2)为了避免发生类型错误, 可以使用 @Override 对覆盖超类的方法进行标记;
@Override
public boolean equals(Object other)
  • A3)如果出现了错误,并且正在定义一个新方法,编译器就会给出 错误报告;如, 假设将下面的声明添加到 Employee类中, 就会看到一个错误报告, 这是因为这个方法并没有覆盖超类Object 中的 任何方法:
@Override
public boolean equals(Employee other)

【3】hashCode 方法

3.1)定义:散列码是由对象导出的一个整型值, 散列码没有规律的;如,x和y 是两个不同的对象, x.hashCode() 和 y.hashCode() 基本上不会相同的;
3.2)String 类的 hashCode 散列码:

  • 3.2.1)String类通过下列算法计算散列码:
int hash = 0;
for(int i=0;i<length(); i++)hash = 31 * hash + charAt(i);
  • 3.2.2) hashCode方法定义在了 Object类, 因此每个对象都有一个默认的散列码, 其值为对象的存储地址:

    对以上打印结果的分析(Analysis):

    • A1)字符串s 和 t 拥有相同的散列码, 这是因为字符串的散列码是由内容导出的, 而字符串缓冲sb 和 tb 却有着不同的散列码,这是因为 在StringBuffer 类中没有定义hashCode 方法,它的散列码是有 Object类的默认 hashCode 方法,以便用户可以 将对象插入到散列表中;
    • A2) hashCode方法应该返回一个整型数值,并合理的组合实例域的散列码, 以便能够让各个不同的对象产生的散列码更加均匀;

3.3)看个荔枝, 下面是 Employee类 的 hashCode方法:

3.4)还可以在java7中做两个改进(improvement):

  • I1) 最好使用 null 安全 的方法 Objects.hashCode , 如果参数为null, 这个方法会返回0, 否则返回对参数调用 hashCode的结果;
  • I2)还有一个方法: 需要组合多个散列值, 可以调用 Objects.hash 并提供多个参数,这个方法会对各个参数调用 Objects.hashCode, 并组合这些散列值; 如 Employee.hashCode 的方法可以简写为:
public int hashCode()
{return Objects.hash(name, salary, hireDay);
}

Attention)

  • A1) equals 方法与 hashCode 的定义必须一致, 如果x.equals(y) 返回 true, 那么 x.hashCode() 就必须与 y.hashCode() 具有相同的值;
  • A2) 如, 如果用定义的Employee.equals 比较雇员的Id, 那么 hashCode就需要散列Id, 而不是 雇员的 姓名或存储地址;

Hint)如果存在数组类型的域, 那么可以使用静态的 Arrays.hashCode 方法计算一个散列码, 这个散列码由数组元素的散列码组成;


【4】toString方法

4.1)随处可见toString 方法的原因是: 只要对象与一个字符串通过操作符 + 连接起来,java编译器就会自动地调用 toString 方法, 以便获得这个对象的字符串描述;
Hint)在调用它 x.toString()的地方可以用 “”+x替代, 这条语句将一个空串与 x 的字符串表示相连接, 这里的x就是 x.toString();
4.2)Object定义了toString()方法,用来打印输出对象所属的类名和散列码;如,

System.out.println(System.out) 


Warning)

  • W1)数组继承了 Object类的 toString方法, 数组类型将按照旧格式打印;
  • W2)修正的方式是调用静态方法 Arrays.toString
  • W3)要想打印多维数组, 需要调用 Arrays.deepToString 方法;

java继承中的 equals + hashCode+toString相关推荐

  1. java 继承 子类 实例化_关于Java继承中父类和子类构造函数的问题

    Java子类在实例化时默认调用的是父类的无参构造函数,不论实例化时调用的是子类的有参还是无参构造函数, 1.当父类没有显式定义构造方法时, 编辑器会默认为此类添加一个隐式无参构造函数.此时子类可以有自 ...

  2. 转转转!java继承中的this和super

    学习java时看了不少尚学堂马士兵的视频,还是挺喜欢马士兵的讲课步骤的,二话不说,先做实例,看到的结果才是最实际的,理论神马的全是浮云.只有在实际操作过程中体会理论,在实际操作过程中升华理论才是最关键 ...

  3. equals, hashCode, toString方法重写,深入探究equals

    首先是超类Employee: package chapter5_inheritance.equals;import java.time.LocalDate; import java.util.Obje ...

  4. Java继承中的子类父类构造方法的调用

    Java的继承中,关于子类和父类构造方法的使用. 子类在初始化时,一定会调用父类的构造方法. 原因如下: 在子类继承父类时,子类会继承父类所有的公共成员变量,公共方法,这些方法在子类中不再重复声明. ...

  5. Java包装类中的equals方法

    基本数据类型包装类中的equals方法用于比对相同包装类中的值是否相等,如果两者比较的包装类类型不同则返回false: 1.基本型和基本型封装型进行"=="运算符的比较,基本型封装 ...

  6. Java继承中成员变量和成员函数的覆盖

    2019独角兽企业重金招聘Python工程师标准>>> 关于继承中变量和方法的覆盖: 1, 与基类中同名的变量,会被派生类所覆盖(同时存在但是有独立的值),直接取值将是派生类的值,但 ...

  7. java可以继承私有的,在java继承中,私有属性能否被继承

    其实在继承中,子类可以继承父类的私有属性的内容空间,但是不能继承父类私有属性的访问权限,下面看代码 父类Animal public class Animal { private String name ...

  8. java .equal_Java中的equals()

    经过几天的学习,终于对equals的用法有了比较全面的认识,并做一个总结. 1.equals的本意--即在Object对象中定义的equals()方法有什么样的意义. (此处先附上==的作用,后面仍有 ...

  9. java继承中的一些该注意的问题

    关于继承,我想大多数人都知道,它是面向对象语言中的三大特性之一,所以在这里,关于继承的概念等我就不做详细介绍了,我主要就讲一下大家对他的认识中一些比较容易犯的错误吧.   错误认识1.继承,是将父类中 ...

最新文章

  1. Sublime Text 3 安装Package Control及配置Python环境
  2. bootstrap 彈窗默認打開_Bootstrap 手册 07 - JS 组件篇
  3. MySQL show binlog events命令查看binlog日志内容
  4. dateformat java 格式_java Date日期类和SimpleDateFormat日期类格式
  5. Mask R-CNN详解和安装
  6. org.hibernate.LazyInitializationException: could not initialize proxy - no Session
  7. 万字长文,带你彻底理解EF Core 5的运行机制,让你成为团队中的EF Core专家
  8. Know more about Oracle Latches
  9. java sql update用法_使用if else条件将SQL UPDATE语句转换为php(codeigniter)
  10. 记录——《C Primer Plus (第五版)》第九章编程练习第十题
  11. Linux 设置 LD_LIBRARY_PATH
  12. 第一章 计算机组成原理 ---- 概述
  13. JMeter自动化测试工具超详细基础讲解(一)
  14. Android 应用瘦身
  15. 六篇经典分割算法汇总
  16. C语言中的指针加减偏移量
  17. Linux系统程序包管理工具-RPM
  18. 利用同义词简化SQL Server 2005开发
  19. 苹果备忘录永久删除怎么恢复?分享2个找回备忘录的高效操作
  20. git代码使用空格缩进

热门文章

  1. 【NOIP2018】赛道修建【二分】【树形dp】【multiset】【贪心】
  2. AGC022E - Median Replace
  3. ADPC2-D 分配颜色
  4. 一起开心集训队第一周训练赛2021/3/14
  5. P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包
  6. [学习笔记] 如果你愿意学那么你是可以看的懂的 —— 群论与 burnside 引理和 polya 定理
  7. AT2675 [AGC018F] Two Trees (构造+二分图染色+并查集)
  8. jzoj6804-NOIP2020.9.26模拟jerry【dp】
  9. P4782-[模板]2-SAT问题【tarjan】
  10. jzoj4743-积木【状压dp】