【Java源码解析】如何严谨地重写 equals 方法、getClass 方法与 instanceof 关键词用法比较_Zhou_LC的博客-程序员宝宝

技术标签: Java  java  后端  开发语言  

如何严谨地重写 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;
    }
}

在这里插入图片描述
在上图输出结果中,前两行结果没有异议,但是后两行就可以看出问题了,发现上述实现的 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 {
    }

在下图的输出结果中,我们可以得出结论,父类实例 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 {
    }

在这里插入图片描述
根据上图结果,我们发现,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;
}

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;
}

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_46360532/article/details/123118780

智能推荐

nginx mime.types php,nginx——mime.types使用案例_越佳美的博客-程序员宝宝

前言:以前只是知道nginx配置文件里面有一个mime.types,但是一直没有用到过,这段时间有需求用到了这个,给大家分享下mime.types的使用。需求:开发人员在开发过程中输出一些调试文件,但是这些文件都是服务器上面,开发没有权限登录服务器获取日志,然后我们使用NFS的方法把所有的输出日志集中到一台服务器上面,然后通过nginx提供web页面的方法,让开发人员可以实时的访问这些调试文件。但...

init(coder:)_2018年《 New Coder》调查:31,000人告诉我们他们如何学习编码并在工作中获得工作…_cumi7754的博客-程序员宝宝

init(coder:)More than 31,000 people responded to our 2018 New Coder Survey, granting researchers an unprecedented glimpse into how adults are learning to code. 超过31,000人对我们的2018年新编码器调查做出了回应,使研究人员对成年人如...

json ----------LitJson解析_july_unity的博客-程序员宝宝

如何添加litJson        两种引入litjson的方法        1,去litjson的网站下载litjson.dll 然后添加引用 找到dll所在目录        2,右键引用 打开管理netget程序包,在联机里面搜索litjson  在搜索结果中选择一个 点击安装使用LitJson解析json文本首先定义我们的json文本[{"id":2,"name":"天...

linux mount remote directory_Change is good的博客-程序员宝宝_mount remote

Linux的mount命令可谓强大,可以将远程机器的目录来挂接到本地作为一个目录存在,以后你就如同访问本地目录一下的访问远程目录。为了达到这个目的你需要做至少两件事情:1.在被mount的机器上做nfs的设定2.在需要mount的机器上做mount设定。 首先来看如何在被mount的机器(PC_A)上做设定。其实这是通过nfs(network file system)来实现

SharedPreferences调用导致的ANR分析_张燕茹的博客-程序员宝宝

转自:http://blog.chinaunix.net/uid-29506893-id-5761774.htmlANR文件提取的有用片段如下:----- pid 13431 at 2016-09-14 11:46:10 -----Cmd line: com.android.settingsat java.lang.Object.wait(Native Me

修改php-fpm和nginx运行用户_akio_medue的博客-程序员宝宝_php-fpm 运行用户

(php)项目a是用test用户运行 nginx和php-fpm是www-data用户运行 (python)项目b是用test用户运行项目a通过php函数exec调用python脚本的接口造成了没有权限访问目录直接把项目b的权限切换为www-data可以执行,但是不便于开发,最好是把php、nginx、项目a、项目b都在一个用户、组下面。打个比方test是当前登录用户 修改nginx的运行角色

随便推点

利用hive对微博数据统计分析案例_sir9ll的博客-程序员宝宝

数据样例:[{“beCommentWeiboId”:”“,”beForwardWeiboId”:”“,”catchTime”:”1387157643”,”commentCount”:”682”,”content”:”喂!2014。。。2014!喂。。。”,”createTime”:”1387086483”,”info1”:”“,”info2”:”“,”info3”:”“,”mlevel”:”“...

matlab中dropoutLayer,Dropout技术介绍 | 文艺数学君_weixin_39981400的博客-程序员宝宝

摘要本篇介绍关于dropout技术。dropout是在过拟合的时候进行使用的技术,适用与deep learning。Dropout在做什么Train时Dropout在做什么当dropout(p)时,意味着每个neuron, 有p%的可能性被去除;(这里需要注意的是,不是去除p%的neuron)我们可以看到的是,每一次进行dropout的时候,网络的结构都会发生改变,会变成一个比之前thin的网络结...

卡尔曼滤波的opencv源码及编程步骤分析_狂奔的飞猪的博客-程序员宝宝

一、卡尔曼滤波的五个方程二、opencv中卡尔曼滤波--KalmanFilter类的源码分析class CV_EXPORTS_W KalmanFilter { public: CV_WRAP KalmanFilter();

R与mongodb_茁壮小草的博客-程序员宝宝

本文系谷歌时发现,代码很好,注释全面,故先码过来,有需要的可以先学习。#http://cran.r-project.org/web/packages/rmongodb/vignettes/rmongodb_introduction.htmllibrary(rmongodb)# Connecting R to MongoDB ------------------------------------

给高特键轴开盖的操作技巧_刻BITTER的博客-程序员宝宝_轴盖怎么打开

装了一套高特银轴之后发现有几个轴的杂音很显眼,忍了几天忍不住了,就开盖润了一下,给弹簧上了点儿润滑脂。没错,还是我那个自行车润滑脂[doge],但是其实效果还不错,只要别上的太多。上太多的话,弹簧音是没了,多了润滑脂的声音~总结一下开盖的小技巧。一开始确实还挺难整的,按下葫芦起了瓢,左边卡扣开了,右边的就又合上了。※ // 工具准备需要又薄又硬的撬片,刀片很合适,普通拆机的塑料撬片根本插不进去;需要手上有指甲,有个两毫米长度应该就很方便了;上盖卡扣很硬,基本上别想直接徒手开。用刀片的话,学校门

推荐文章

热门文章

相关标签