当前位置: 首页 > 科技观察

不小心踩到了龙目岛的陷阱?

时间:2023-03-18 21:04:56 科技观察

介绍今天中午带着耳机在代码的世界里穿梭,在群里被@操作,怒问,我是不是删除了最近产生的用户数据?我礼貌地回答没有?怎么能随便删除生产数据呢?这是公司的红线。另外,我没有删除数据库的权限,但是我还有查询权限。赶紧上堡垒机,再去生产数据库查数据。检查后,数据还在。我吓死了。如果数据仍然存在,问题就不是什么大问题。这无非是应用程序的问题。赶紧打开代码查看。为什么少了一条用户数据?看了代码,好像没什么问题。这是一个比较简单的逻辑。数据直接从DB通过分页查询到前端,然后由前端负责展示。没有复杂的逻辑。我想肯定是前端的问题,肯定是他没有展示数据,立马把问题扔给他,让他配合看看是不是前端的问题,然后我也仔细看了代码,不到一分钟前端说他显示的数据没有问题,而且都是后端给的,一个数据都没有漏。那是一个后端错误。从肉眼看来,似乎可能的问题是分页导致的数据丢失。不过这个分页插件是全公司都用的,应该没有问题。如果找不到问题,只能请测试人员在测试环境下试一下,看能否重现。仔细看一下测试环境的重现,其实是有去重的方法的,而且去重逻辑比较简单,就是把list转成set去重。看来这个去重方法有问题。我大致写了一个单元测试。模仿产生的数据,大致逻辑如下:publicstaticvoidmain(String[]args){SetuserSet=newHashSet<>();UserDTOuserDTO=newUserDTO();userDTO.setId(1);userDTO.setUserName(“java财务”);UserDTOuserDTO1=newUserDTO();userDTO1.setId(2);userDTO1.setUserName(“java财务”);userSet.add(userDTO);userSet.add(userDTO1);System.out.println(userSet.size());System.out.println(userDTO1.equals(userDTO));}@DatastaticclassUserDTOextendsBaseDTO{privateStringuserName;}@DatastaticclassBaseDTO{privateIntegerid;}我们可以输出结果集set的长度为1,user1和user2都是相等,显然两个用户的ID不同。为什么他们相等?我们知道set是可以去重的,因为Set的操作是通过操作map来实现的。set的add其实就是调用了map的put方法,map的put方法相信大家应该都看过它的源码,这里就不赘述了。大致过程是通过哈希算法通过key定位到数组的下标,先判断key的hash是否相等,如果相等,再判断key的值是否相等,如果相等相等,原值将被覆盖。我们上面的例子是因为对象的hash和value是相等的,而我们的两个对象user1和user2应该是不相等的,因为id不相等,那为什么相等呢?我们仔细看一下上面的代码,我们使用读取lombok中的@Data注解后,可以看到这个注解帮我们生成了哪些方法。通过上面的对比我们可以看出@Data注解已经帮我们生成了类上的注解,提供了get、set、equals、hashCode、canEqual、toString方法,这个注解真的很方便。上面的bug是因为它生成的equals方法有问题。我们可以编译上面的代码,然后复制类中生成的equals方法看看。通过上面生成的代码,我们可以看到equals方法只比较了userName字段,即当前类的字段,和父类的字段不做比较。这就是两个对象相等的原因。既然找到了问题所在,那么解决问题就比较简单了。解决问题手动重写equals和hashCode方法。这种方法绝对不推荐。既然我们使用了lombok,就是为了解放我们的双手,让代码更加简洁。在比较类中添加@EqualsAndHashCode(callSuper=true)callSuper=true会包含父类的equals和hashCode方法。我们可以比较带有@EqualsAndHashCode(callSuper=true)和没有此注解的equals方法。代码差异。图片上的区别还是很明显的。添加@EqualsAndHashCode(callSuper=true)会调用父类的equals方法进行比较,所以这个注解也可以解决这个问题。通过这种方式添加注解确实可以解决问题,但是这个注解必须要在每个类中添加,也是比较费力的工作。我们可以寻找其他的解决办法,比如有没有配置文件设置之类的,然后全局生效。最后通过查询资料,发现我们写了一个lombok.config配置文件,放在了我们项目的根目录下。lombok.equalsAndHashCode.callSuper=call中写的内容相当于@EqualsAndHashCode(callSuper=true),所以我们不会在每个类中都需要添加这个注释,相当于在类中添加@EqualsAndHashCode(callSuper=true)本项目下使用@Data注解的类,可以通过配置文件全局生效。综上所述,我们再回顾一下上面的问题。归根结底是对象的equals方法使用不当造成的。因此,如果判断自定义对象业务相等,则需要重写hashCode和equals方法。改写的时候,我们可以通过idea来生成,生成之后最好看一下,看生成的是否符合我们的业务需求。我们在工作中操作一些常见的容器类如Set、Map等来实现自己的一些业务。我们仍然需要查看它们的源代码。比如我们使用Set去重。如果我们使用Forcustomobjects,如果不重写hashCode和equals方法,去重会失败。只有了解它,我们才能真正用好它。在阿里巴巴的开发手册中关于hashCode和equals也有明确的说明lombok用起来相当爽,但是还是有一些细节需要注意。在使用之前,可以粗略查看其官网提供的内容,否则莫名其妙的问题不知如何下手。这有点类似于我们使用SpringBoot。用起来很爽,但是如果遇到莫名其妙的bug,解决起来就很头疼了。最后再回顾一下比较常见的几道关于hashCode和equals的面试题?其实如果我们只需要阅读HashMap的源码,下面的面试题还是很简单的。什么情况下需要重写方法呢?如果只重写equals方法而不重写HashCode呢?equals、==和hashcode()有什么区别?二维码关注。转载本文请联系java财经公众号。