08 | 判等问题:程序里如何确定你就是你?
朱晔
该思维导图由 AI 生成,仅供参考
你好,我是朱晔。今天,我来和你聊聊程序里的判等问题。
你可能会说,判等不就是一行代码的事情吗,有什么好说的。但,这一行代码如果处理不当,不仅会出现 Bug,还可能会引起内存泄露等问题。涉及判等的 Bug,即使是使用 == 这种错误的判等方式,也不是所有时候都会出问题。所以类似的判等问题不太容易发现,可能会被隐藏很久。
今天,我就 equals、compareTo 和 Java 的数值缓存、字符串驻留等问题展开讨论,希望你可以理解其原理,彻底消除业务代码中的相关 Bug。
注意 equals 和 == 的区别
在业务代码中,我们通常使用 equals 或 == 进行判等操作。equals 是方法而 == 是操作符,它们的使用是有区别的:
对基本类型,比如 int、long,进行判等,只能使用 ==,比较的是直接值。因为基本类型的值就是其数值。
对引用类型,比如 Integer、Long 和 String,进行判等,需要使用 equals 进行内容判等。因为引用类型的直接值是指针,使用 == 的话,比较的是指针,也就是两个对象在内存中的地址,即比较它们是不是同一个对象,而不是比较对象的内容。
这就引出了我们必须必须要知道的第一个结论:比较值的内容,除了基本类型只能使用 == 外,其他类型都需要使用 equals。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了Java中的判等问题,重点讨论了equals、compareTo以及Java的数值缓存和字符串驻留等方面。文章首先介绍了equals和==的区别,强调了对于引用类型需要使用equals进行内容判等,而对于基本类型则只能使用==。随后通过对Integer和String的判等问题进行深入分析,解释了使用==可能得到不同结果的原因,以及Java的数值缓存和字符串驻留机制对判等结果的影响。文章还通过实际案例展示了在业务代码中使用==进行判等可能引发的问题,以及如何避免这些问题的发生。通过深入的技术分析和实际案例,全面解析了Java中判等问题的原理和解决方法,对于开发人员理解和避免判等问题具有重要的指导意义。文章还提到了实现equals方法需要注意的点,以及hashCode和equals需要配对实现的问题,并给出了相应的解决方法。文章内容丰富,涵盖了Java中判等问题的方方面面,对于读者快速了解Java中判等问题的原理和解决方法具有重要参考价值。文章还通过具体案例和技术细节,提醒读者在实际开发中需要注意的细节和陷阱,对于读者的实际开发工作具有重要的指导意义。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(25)
- 最新
- 精选
- Darren稍微补充一点,可能因为篇幅的原因,老师没提到,关于equals其实还有一个大坑,equals比较的对象除了所谓的相等外,还有一个非常重要的因素,就是该对象的类加载器也必须是同一个,不然equals返回的肯定是false;之前遇到过一个坑:重启后,两个对象相等,结果是true,但是修改了某些东西后,热加载(不用重启即可生效)后,再次执行equals,返回就是false,因为热加载使用的类加载器和程序正常启动的类加载器不同。关于类加载器部分,JDK 9 之前的 Java 应用都是由「启动类加载器」、「扩展类加载器」、「应用程序类加载器」这三种类加载器互相配合来完成加载的,如果有需要还可以加入自定义的类加载器来进行拓展;JDK 9 为了模块化的支持,对双亲委派模式做了一些改动:扩展类加载器被平台类加载器(Platform ClassLoader)取代。平台类加载器和应用程序类加载器都不再继承自 java.net.URLClassLoader,而是继承于 jdk.internal.loader.BuiltinClassLoader。具体细节可以自行搜索。 现在回答下问题: 第一个问题: instanceof进行类型检查规则是:你是该类或者是该类的子类; getClass获得类型信息采用==来进行检查是否相等的操作是严格的判断。不会存在继承方面的考虑; 第二个问题: HashSet本质上就是HashMap的key组成的不重复的元素集合,contains方法其实就是根据hashcode和equals去判断相等的 TreeSet本质TreeMap的key组成的,数据结构是红黑树,是自带排序功能的,可以在放入元素的时候指定comparator(比较器),或者是放入的元素要实现Comparable接口后元素自己实现compareTo方法,contains方法是根据比较器或者compareTo去判断相等
作者回复: 👍🏻👍🏻👍🏻 这位同学作为本课课代表 😀
2020-03-2627299 - 👽2 . HashSet 底册是HashMap。TreeSet底层是TreeMap HashSet就是使用HashMap调用equals,判断两对象的HashCode是否相等。 TreeSet因为是一个树形结构,则需要考虑数的左右。则需要通过compareTo计算正负值,看最后能否找到compareTo为0的值,找到则返回true。 简单来说,TreeSet底层使用compareTo方法比较,HashSet底层使用hash值比较。
作者回复: 👍🏻
2020-03-2623 - Sun老师的课程,真的是干货,每天凌晨更新完看一遍,早上上班前在看一遍,感受都不一样,期待出更多干货,共同进步
作者回复: 设计篇和安全篇还会有更丰富的内容,跟紧脚步,细细品味
2020-03-2612 - 东方奇骥看到这节,说起Lombok,老师觉得Lombok 适合用于生产环境吗?之前一直都是自己业余练习使用,但是工作中项目都还是没有使用。
作者回复: 只要你理解它各种注解会生成怎样的代码,就没问题
2020-03-264 - pedro1楼的回答已经趋于完美,我也翻了一下 JDK 源码,HashSet 的本质是 HashMap,会通过 hash 函数来比较值,TreeSet 的本质是 TreeMap 会通过 compareTo 比较。 至于类加载器的问题,我想这个不好复现,有没有楼下的小伙伴补充一下的。
作者回复: 👍🏻
2020-03-2644 - 阿郑老师的每一篇文章都是满满的干货呀,手动点赞👍👍👍
作者回复: 觉得好可以多转发分享
2020-03-263 - James每一个案例都是独立的SpringBoot或Java命令行应用程序,可以单独启动,避免相互干扰 请问下,为啥我启动的是独立main类为啥还会启动其他无关这个包的类.比如连接redis
作者回复: 依赖是一起的
2020-07-112 - Huodefa_0426老师,文中你说:在启动程序时设置 JVM 参数 -XX:+PrintStringTableStatistics,程序退出时可以打印出字符串常量表的统计信息。调用接口后关闭程序,输出如下。我设置了关闭程序怎么没看见输出的信息,是输出在控制台还是在日志文件中?如果是文件 是哪个文件?
作者回复: 控制台,确保参数生效了
2020-03-2622 - yihang补充一点,浮点数的==比较也有坑,跟浮点数小数精度有关
作者回复: 是的,这个下篇提到了
2020-03-281 - justin老师您好,就是关于这个例子,加了intern()会在常量池驻留,导致每个bucket size变得很大。如果没有加intern()方法的时候,Average bucket size就变得很低,测试的是0.276,这个帮忙解释下 @GetMapping("internperformance") public int internperformance(@RequestParam(value = "size", defaultValue = "10000000")int size) { //-XX:+PrintStringTableStatistics //-XX:StringTableSize=10000000 long begin = System.currentTimeMillis(); list = IntStream.rangeClosed(1, size) .mapToObj(i-> String.valueOf(i).intern()) .collect(Collectors.toList()); log.info("size:{} took:{}", size, System.currentTimeMillis() - begin); return list.size(); }
作者回复: 因为没有进常量池
2020-04-02
收起评论