首页 > 编程语言 >【Java源码解析】如何严谨地重写 equals 方法、getClass 方法与 instanceof 关键词用法比较

【Java源码解析】如何严谨地重写 equals 方法、getClass 方法与 instanceof 关键词用法比较

时间:2023-01-11 09:11:25浏览次数:63  
标签:instanceof Java equals Person 源码 Student obj public

【Java源码解析】如何严谨地重写 equals 方法、getClass 方法与 instanceof 关键词用法比较
https://blog.csdn.net/m0_46360532/article/details/123118780

文章目录

如何严谨地重写 equals 方法

1 equals 方法概述

equals 方法我们都非常熟悉,equals 是 Object 基类中的模板方法 ,每个类中都有它的的存在,多数类或其抽象父类都以不同方式重写了 equals 方法。

例如,作为所有数值封装类的父类 Number 类,将 equals 比较方法的重写放到了各数值封装类中进行,因为不同数值类的相等判别依据不同,而 ArrayList 类和 LinkedList 类则将 equals 方法实现放到了其抽象父类 AbstractList 类中实现,因为集合框架得益于迭代器模式,因此可以方便地遍历同一类集合实现类,屏蔽其遍历细节,因此这两个集合类便可以在其抽象父类中实现相同的 equals 方法,等等这样的例子还有很多…
在这里插入图片描述

2 String 类中的 equals 方法

我们以 String 类中的 equals 方法为例,进行自定义 equals 方法的学习。

首先是一个引用是否一直的判断,之后便是根据 String 类型的特殊性,只要字符串一样就可以相等,不必硬性要求是否是同一个 String 对象,因此后续又进行了 instanceof 关键词的类别判断,首先需要判断参数类是否是字符串类,这才是后续可以获取其字符数组的前提,而每个 String 类,在 Java 中其实都是用一个 char[] value 去存储的,因此之后我们遍历当前字符串、目标字符串的 value 数组,用一次遍历,O(n) 的时间复杂度来进一步判断这是否是两个值相同的字符串。
在这里插入图片描述

3 自定义 equals 方法时出现的问题

我们用 Person 类和 Student 类,两个类来进行问题的阐释,同时我们仿照 JDK 源码中 String 类这个例子的 equals 方法的编写思想,重写我们自定义的这两个类中的 equals 方法:

public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("001");
        Person person11 = new Person("001");
        System.out.println("person1.equals(person11) = " + person1.equals(person11));
        Student student1 = new Student("001", 100);
        Student student11 = new Student("001", 100);
        System.out.println("student1.equals(student11) = " + student1.equals(student11));
        System.out.println("person1.equals(student1) = " + person1.equals(student1));
        System.out.println("student1.equals(person1) = " + student1.equals(person1));
    }
}
class Person {
    private String idCard;
    public Person() {
    }
    public Person(String name) {
        this.idCard = name;
    }
    public String getIdCard() {
        return idCard;
    }
    public void setIdCard(String name) {
        this.idCard = name;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof Person) { //obj为null也可以判断,这里不用单独处理
            Person anotherPerson = (Person) obj;
            return this.getIdCard().equals(anotherPerson.getIdCard());
        }
        return false;
    }
}
class Student extends Person {
    private Integer score;
    public Student() {
    }
    public Student(String idCard, Integer score) {
        super(idCard);
        this.score = score;
    }
    public Integer getScore() {
        return score;
    }
    public void setScore(Integer score) {
        this.score = score;
    }
    @Override
    public boolean equals(Object obj) {
        if (!super.equals(obj)) {
            return false;
        }
        if (obj instanceof Student) {
            Student anotherStudent = (Student) obj;
            return this.getScore() == anotherStudent.getScore();
        }
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

在这里插入图片描述
在上图输出结果中,前两行结果没有异议,但是后两行就可以看出问题了,发现上述实现的 equals 方法不满足对称性,也就是说 A.equals(B) != B.equals(A),而造成这种问题的原因就是,我们令 A 和 B 不再是同一种类型,而是父子类关系,而 instanceof 关键词在判断父子类时是很“认真”的,也就是说代码中的这一句是问题的关键——obj instanceof Student,由于 student1.equals(person1) 我们传入的参数是 Person 类型的,因此 obj instanceof Student 为 false,自然就判定为不相等了,更直观的验证请看下面的 二。

4 instanceof 关键词与 getClass 方法的比较

public class Test2 {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person instanceof Student);
        Student student = new Student();
        System.out.println(student instanceof Person);
    }
}
class Person {}
class Student extends Person {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在下图的输出结果中,我们可以得出结论,父类实例 instanceof 子类类型会返回 false,而子类实例 instanceof 父类类型会返回 true,因此,这就更加印证了 一部分 中我们最后一行输出为什么是 false,我们的 equals 方法为什么不满足对称性。
在这里插入图片描述

那还有什么方法能判断类型呢,那我们自然也能想到是 getClass 方法了,之后,我们用 getClass 方法作测试,这也是基类 Object 类中的方法,我们可以获取当前类的运行时类型,我们接下来用这个方法进行上述 instanceof 不能解决的父子类对称性问题的检验

public class Test3 {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.getClass());
        Student student = new Student();
        person = student;
        System.out.println(person.getClass());
    }
}
class Person {}
class Student extends Person {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述
根据上图结果,我们发现,getClass 方法获取的是实际的类型,不会因为上转型的问题而出现像是 instanceof 那样的对称性问题

5 正确编写 equals 方法

我们接下来便可以改进我们 Person、Student 类中的 equals 方法了(由于主要代码上文全部展示过,因此这里只展示我们改进的那部分代码):

Person 类

@Override
public boolean equals(Object obj) {
      if (this == obj) {
          return true;
      }
      if (getClass() == obj.getClass()) { //obj为null也可以判断,这里不用单独处理
          Person anotherPerson = (Person) obj;
          return this.getIdCard().equals(anotherPerson.getIdCard());
      }
      return false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Student 类

@Override
public boolean equals(Object obj) {
    if (!super.equals(obj)) {
        return false;
    }
    if (getClass() == obj.getClass()) {
        Student anotherStudent = (Student) obj;
        return this.getScore() == anotherStudent.getScore();
    }
    return false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述
再次运行,我们发现,equals 方法已经满足对称性,而又因为它也同时满足自反性,传递性、一致性(模拟代码便可证得),我们这样写的 equals 方法,才是正规的经得起实践的 equals 方法。因此,我们今后重写 equals 方法时,要根据实际情况判断是否用 instanceof 关键词判断数据类型,还是用 getClass 方法来判断数据类型,并进行自反性,对称性、传递性、一致性以及具体项目等情况的判断,才能编写出正确的 equals 方法。

标签:instanceof,Java,equals,Person,源码,Student,obj,public
From: https://www.cnblogs.com/sunny3158/p/17042806.html

相关文章

  • Java学习路线总结
    认清现实,放弃幻想。(特此感谢动力节点老杜分享出来的学习路线)学习阶段阶段地址第一阶段:JavaSE基础入https://www.cnblogs.com/NorthPoet/p/1702......
  • JavaScript所有内部属性列表 [[Configurable]] 等
    简介据MDN文档所说内部属性是由[[···]]包裹的内容,于是我们去复制ECMA-262标准的所有文字部分,然后用正则统计[[···]]的出现并打印效果代码经老大提醒,这里......
  • Java课程设计之——Web前端设计
    0、团队课程设计1、主要使用的技术HTML/CSSBootstrap5javascript/jQuery/jQuery-UIJSP2、前期调查与设计搜索引擎主要分为两个界面,主界面和搜索结果界面主界面......
  • Java中集合函数--ArrayList和HashMap
    ArrayList集合框架:ArrayList是一个数组队列,提供了相关的添加、删除、修改、遍历等功能先初始化对象:ArrayList<String>list=newArrayList<>();添加元素使用add()方法......
  • java企业人事管理系统企业工资考勤系统
    简介Java基于ssm开发的企业人事考勤工资系统,员工可以打卡、请假。系统根据员工的打卡情况自动计算工资(全勤、请假、旷工、加班、迟到、早退等计算出最终实发工资),员工还可......
  • 【RocketMQ】负载均衡源码分析
    RocketMQ在集群模式下,同一个消费组内,一个消息队列同一时间只能分配给组内的某一个消费者,也就是一条消息只能被组内的一个消费者进行消费,为了合理的对消息队列进行分配,于是......
  • Java线程诊断
    1.诊断CPU占用过高[root@master~]#nohupjava-jarJvmProject.jar&//有问题的java程序[1]1627[root@master~]#nohup:ignoringinputandappendingoutputto......
  • java:年号字串-------进制转换
    题目描述小明用字母 A 对应数字 1,B 对应 2,以此类推,用 Z 对应 26。对于 27 以上的数字,小明用两位或更长位的字符串来对应,例如 AA 对应 27,AB 对应 28,AZ......
  • Java课程设计之——Elasticsearch篇
    0、团队项目博客1、主要使用的技术及开发工具Elasticsearch7.17.3RESTAPIElasticsearchjavaAPIClient7.17.3Kibana7.17.3Jackson2.12.32、Elasticsearch......
  • JavaScript学习笔记—运算符
    运算符(操作符):可以用来对一个或多个操作数(值)进行运算1.算术运算符:+加法运算符-减法运算符*乘法运算符/触发运算符**幂运算%模运算算术运算时,除了字符串的加......