Java 业务开发常见错误 100 例
朱晔
贝壳金服资深架构师
51940 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
代码篇 (23讲)
Java 业务开发常见错误 100 例
15
15
1.0x
00:00/00:00
登录|注册

08 | 判等问题:程序里如何确定你就是你?

你好,我是朱晔。今天,我来和你聊聊程序里的判等问题。
你可能会说,判等不就是一行代码的事情吗,有什么好说的。但,这一行代码如果处理不当,不仅会出现 Bug,还可能会引起内存泄露等问题。涉及判等的 Bug,即使是使用 == 这种错误的判等方式,也不是所有时候都会出问题。所以类似的判等问题不太容易发现,可能会被隐藏很久。
今天,我就 equals、compareTo 和 Java 的数值缓存、字符串驻留等问题展开讨论,希望你可以理解其原理,彻底消除业务代码中的相关 Bug。

注意 equals 和 == 的区别

在业务代码中,我们通常使用 equals 或 == 进行判等操作。equals 是方法而 == 是操作符,它们的使用是有区别的:
对基本类型,比如 int、long,进行判等,只能使用 ==,比较的是直接值。因为基本类型的值就是其数值。
对引用类型,比如 Integer、Long 和 String,进行判等,需要使用 equals 进行内容判等。因为引用类型的直接值是指针,使用 == 的话,比较的是指针,也就是两个对象在内存中的地址,即比较它们是不是同一个对象,而不是比较对象的内容。
这就引出了我们必须必须要知道的第一个结论:比较值的内容,除了基本类型只能使用 == 外,其他类型都需要使用 equals
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《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去判断相等

    作者回复: 👍🏻👍🏻👍🏻 这位同学作为本课课代表 😀

    27
    291
  • 👽
    2 . HashSet 底册是HashMap。TreeSet底层是TreeMap HashSet就是使用HashMap调用equals,判断两对象的HashCode是否相等。 TreeSet因为是一个树形结构,则需要考虑数的左右。则需要通过compareTo计算正负值,看最后能否找到compareTo为0的值,找到则返回true。 简单来说,TreeSet底层使用compareTo方法比较,HashSet底层使用hash值比较。

    作者回复: 👍🏻

    22
  • Sun
    老师的课程,真的是干货,每天凌晨更新完看一遍,早上上班前在看一遍,感受都不一样,期待出更多干货,共同进步

    作者回复: 设计篇和安全篇还会有更丰富的内容,跟紧脚步,细细品味

    11
  • 东方奇骥
    看到这节,说起Lombok,老师觉得Lombok 适合用于生产环境吗?之前一直都是自己业余练习使用,但是工作中项目都还是没有使用。

    作者回复: 只要你理解它各种注解会生成怎样的代码,就没问题

    4
  • pedro
    1楼的回答已经趋于完美,我也翻了一下 JDK 源码,HashSet 的本质是 HashMap,会通过 hash 函数来比较值,TreeSet 的本质是 TreeMap 会通过 compareTo 比较。 至于类加载器的问题,我想这个不好复现,有没有楼下的小伙伴补充一下的。

    作者回复: 👍🏻

    4
    4
  • 阿郑
    老师的每一篇文章都是满满的干货呀,手动点赞👍👍👍

    作者回复: 觉得好可以多转发分享

    3
  • James
    每一个案例都是独立的SpringBoot或Java命令行应用程序,可以单独启动,避免相互干扰 请问下,为啥我启动的是独立main类为啥还会启动其他无关这个包的类.比如连接redis

    作者回复: 依赖是一起的

    2
  • Huodefa_0426
    老师,文中你说:在启动程序时设置 JVM 参数 -XX:+PrintStringTableStatistics,程序退出时可以打印出字符串常量表的统计信息。调用接口后关闭程序,输出如下。我设置了关闭程序怎么没看见输出的信息,是输出在控制台还是在日志文件中?如果是文件 是哪个文件?

    作者回复: 控制台,确保参数生效了

    2
    2
  • yihang
    补充一点,浮点数的==比较也有坑,跟浮点数小数精度有关

    作者回复: 是的,这个下篇提到了

    1
  • 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(); }

    作者回复: 因为没有进常量池

收起评论
显示
设置
留言
25
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部