资讯动态

Java 的 == 和 equals 到底有什么区别?用 String、Integer 和自定义对象一次讲清

发布时间:2026/10/5 11:05:16 来源:尧图企业网站定制
快速认识和equals不是“一个比较地址一个比较内容”这么简单。更准确的规则是两边是基本类型时比较值。两边是对象引用时判断它们是否指向同一个对象。equals是一个方法。Object里的默认实现就是引用比较但String、包装类等会重写它。自定义类如果只重写equals、不重写hashCode放进HashSet或作为HashMap的 Key 时仍然可能出错。可以先记住一张判断表场景推荐写法原因比较两个int、long、doublea b基本类型没有equals比较两个字符串的内容a.equals(b)或Objects.equals(a, b)字符串的equals已按内容实现判断两个引用是否为同一个对象a b这正是引用比较的语义枚举值比较enumValue OtherEnum.VALUE枚举常量是单例更安全也更直观可能为null的对象值比较Objects.equals(a, b)避免空指针自定义实体按业务字段判等重写equals和hashCode保证集合和 Map 行为一致先用一个最小例子看结果StringaJava;StringbJava;StringcnewString(Java);System.out.println(ab);// trueSystem.out.println(ac);// falseSystem.out.println(a.equals(c));// truea b为true是因为两个字面量可以复用同一个字符串对象。c是新创建的对象所以a c为false。它们的内容都相同所以a.equals(c)为true。拓展一、先区分基本类型和引用类型基本类型保存的是具体数值引用类型保存的是指向对象的引用。intleft10;intright10;System.out.println(leftright);// true这里比较的是两个int的值所以结果是true。对于引用StringleftnewString(Java);StringrightnewString(Java);System.out.println(leftright);// falseSystem.out.println(left.equals(right));// truenew String创建了两个不同的对象。判断它们是否为同一个对象结果是falseequals比较字符串内容结果是true。所以不要把“对象用equals”背成无条件规则。真正要问的是我现在比较的是对象身份还是对象的业务值二、为什么字符串字面量的 有时是 true直接写出的字符串字面量会进入字符串常量池。同一组在编译期确定的字符串内容可以被两个变量引用到同一个对象。StringaJava;StringbJava;StringcJava;System.out.println(ab);// trueSystem.out.println(ac);// trueJa va的两个操作数都是编译期常量编译器会在编译阶段完成拼接所以它仍然可以复用Java的常量对象。但下面两种情况不一样StringprefixJa;Stringdprefixva;StringenewString(Java);System.out.println(ad);// falseSystem.out.println(ae);// falseSystem.out.println(a.equals(d));// trueSystem.out.println(a.equals(e));// trueprefix是变量它的值要到运行时才能确定。这里是否创建新对象、是否进入常量池不能只凭肉眼猜。实际写业务代码时字符串内容比较就统一使用equals或Objects.equals不要依赖常量池带来的结果。三、Integer 的 127 和 128下面这段代码很容易让初学者疑惑Integera127;Integerb127;Integerc128;Integerd128;System.out.println(ab);// trueSystem.out.println(cd);// falseSystem.out.println(c.equals(d));// true自动装箱会调用类似Integer.valueOf(127)的方法。HotSpot 常见实现会缓存-128到127之间的Integer对象所以a和b可能指向缓存中的同一个对象128超出这个常见范围c和d可能是两个不同对象。这里要特别注意两点包装类比较的是对象不能把当成数值比较。Integer和Long即使数值相同equals也会因为类型不同而返回false。Integercount1000;LongsameValue1000L;System.out.println(count.equals(sameValue));// false业务代码中比较包装类数值优先使用equals并且确认两边类型一致。需要做数值计算时先明确是否可能为null再决定如何拆箱或转换。四、自定义对象必须自己说明“什么叫相等”如果类没有重写equals它继承的是Object.equals效果等同于引用比较。classPlainUser{privatefinalStringname;PlainUser(Stringname){this.namename;}}PlainUserleftnewPlainUser(Tom);PlainUserrightnewPlainUser(Tom);System.out.println(left.equals(right));// false两个用户的name相同但这是两个不同对象所以默认比较结果仍然是false。如果业务规定“用户名相同就是同一个用户”就需要自己重写。importjava.util.Objects;classNamedUser{privatefinalStringname;NamedUser(Stringname){this.namename;}Overridepublicbooleanequals(Objectother){if(thisother){returntrue;}if(!(otherinstanceofNamedUser)){returnfalse;}NamedUserthat(NamedUser)other;returnObjects.equals(name,that.name);}OverridepublicinthashCode(){returnObjects.hash(name);}}这段代码做了三件事先判断是否为同一个对象减少后续比较。判断类型是否匹配。用Objects.equals比较参与判等的字段顺便处理字段为null的情况。为什么还要重写hashCode因为HashSet、HashMap会先根据哈希值寻找桶再用equals确认是否相等。如果两个对象被判定为equals但哈希值不同集合就可能把它们放进不同位置。SetNamedUserusersnewHashSet();users.add(newNamedUser(Tom));System.out.println(users.contains(newNamedUser(Tom)));// true这里的contains为true不是因为两个对象是同一个对象而是因为类已经定义了“姓名相同就算相同用户”并且equals与hashCode使用了同一组字段。五、判断路径可以简化成这样写代码时不需要每次都想一遍底层实现可以按下面的顺序判断两边是基本类型 - 用 两边是对象 - 需要判断是不是同一个对象用 - 需要按内容或业务字段比较用 equals / Objects.equals - 比较的是枚举常量用 - 自己定义的实体需要进入 Set / Map重写 equals hashCode六、几个容易踩的坑第一个坑对可能为 null 的变量直接调用 equals。Stringleftnull;StringrightJava;// System.out.println(left.equals(right)); // NullPointerExceptionSystem.out.println(Objects.equals(left,right));// false如果你能确定常量非空也可以写成Java.equals(variable)。如果两边都可能为空Objects.equals更清楚。第二个坑只重写 equals不重写 hashCode。这在普通if判断里可能看不出来但加入HashSet或HashMap后就会暴露。只要重写其中一个就应该同时检查另一个。第三个坑BigDecimal 不能用 equals 判断数值相等。BigDecimalanewBigDecimal(1.0);BigDecimalbnewBigDecimal(1.00);System.out.println(a.equals(b));// falseSystem.out.println(a.compareTo(b));// 0equals会同时考虑数值和精度compareTo比较数值大小。金额比较采用哪个方法取决于业务是否关心精度表示。不要看到false就立刻认为compareTo永远更合理先把业务口径写清楚。第四个坑数组用 equals 比较内容。数组没有按元素重写equals。要比较两个数组的内容应使用Arrays.equals二维数组使用Arrays.deepEquals。七、练习题先不要运行代码自己写下结果再对照输出。练习一字符串Stringajava;StringbnewString(java);Stringcjava;System.out.println(ab);System.out.println(ac);System.out.println(a.equals(b));重点不是背答案而是解释每一行为什么是true或false。练习二IntegerIntegera100;Integerb100;Integerc200;Integerd200;System.out.println(ab);System.out.println(cd);System.out.println(c.equals(d));再把这四个变量改成new Integer(...)的写法比较结果会如何变化注意new Integer已经不再推荐使用这里只用来观察对象身份。练习三自定义对象创建一个Student类包含id和name。规定id相同就是同一学生完成重写equals和hashCode。创建两个id相同、name不同的对象。放入HashSet观察集合大小。调用contains解释结果。删掉hashCode重新运行观察变化。练习四找错误下面这段代码可能在什么时候出错MapString,IntegerscoresnewHashMap();Stringkeynull;scores.put(key,90);System.out.println(scores.get(null));先查HashMap对nullKey 的说明再结合自己的 JDK 版本运行验证。不要把“HashMap 一定不能存 null”当成未经核对的结论。八、B站和练习资源怎么用B站适合用它看老师怎么把对象、引用和集合串起来但不要只开着视频“听懂”。每看完一段就把示例里的一个变量改掉重新运行看看结果是否和预测一致。可以按这个顺序检索B站搜索“Java equals 和 的区别”先看一个 10 到 20 分钟的入门讲解。搜索“Java hashCode equals”确认集合中为什么需要成对重写。搜索“Java String 常量池”理解字面量和new String的差异。搜索“Java Integer 缓存”观察自动装箱的执行路径。回到代码把文章中的四个练习全部跑一遍。可直接打开这些入口B站Java equals 和 的区别B站Java hashCode equalsOracleObject.equalsOracleObject.hashCodeHackerRank Java 练习CodingBat Java 练习最后给一个实用规则如果比较的是对象身份用如果比较的是值或业务字段用equals或Objects.equals如果自定义对象要进入HashSet、HashMap把equals和hashCode一起检查。先把这三个判断用到自己的代码里比一次背完所有细节更有用。标签Java、equals、Java基础、字符串、HashMap

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑