JVM面试题系列:两个对象值相同(x.equals(y) == true),但是可能存在hashCode不同吗?

面试官的考察点

这道题仍然是考察JVM层面的基本知识,面试官认为,基本功扎实,才能写出健壮性和稳定性很高的代码。

涉及到的技术知识

(x.equals(y)==true),这段代码,看起来非常简单,但其实里面还是涉及了一些底层知识点的,首先我们基于equals这个方法进行探索。

equals这个方法,在每个对象中都存在,以String类型为例,其方法定义如下

public boolean equals(Object anObject) {
  if (this == anObject) { 
    return true;
  }
  if (anObject instanceof String) { //判断对象实例是否是String
    String anotherString = (String)anObject; //强转成string类型
    int n = value.length;
    if (n == anotherString.value.length) { //如果两个字符串相等,那么它们的长度自然相等。
      //遍历两个比较的字符串,转换为char类型逐个进行比较。
      char v1[] = value;  
      char v2[] = anotherString.value;
      int i = 0;
      while (n-- != 0) {
        if (v1[i] != v2[i]) //采用`==`进行判断,如果不相同,则返回false
          return false;
        i++;
      }
      return true; //否则返回true。
    }
  }
  return false;
}

首先来分析第一段代码,判断传递进来的这个对象和当前对象实例this是否相等,如果相等则返回true

  if (this == anObject) { 
    return true;
  }

==号的处理逻辑是怎么实现的呢?

了解==判断

在java语言中==操作符号,这个比较大家都知道,是基于引用对象的比较,具体其实还有一些其他的区别。

JVM会根据==两边相互比较的操作类型不同,在编译时生成不同的指令。

  1. 对于boolean,byte、short、int、long这种整形操作数,会生成if_icmpne指令,该指令用于比较整形数值是否相等。关于if_icmpne指令可以参见:Chapter 4. The class File Format,它在Hotspot VM中的bytecodeInterpreter源码中的具体实现如下

    #define COMPARISON_OP(name, comparison)                                      
    CASE(_if_icmp##name): {                                                
      int skip = (STACK_INT(-2) comparison STACK_INT(-1))                
        ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3;             
      address branch_pc = pc;                                            
      UPDATE_PC_AND_TOS(skip, -2);                                       
      DO_BACKEDGE_CHECKS(skip, branch_pc);                               
      CONTINUE;                                                          
    }

    可以看到实质是按照comparison表达式比较操作数栈中偏移量为-1和-2的两个INT值。

  2. 如果操作数是对象的话,编译器则会生成if_acmpne指令,与if_icmpne相比将i(int)改成了a(object reference)。这个指令在JVM规范中的表述:Chapter 4. The class File Format,它在Hotspot VM中相应的实现可参考:

    COMPARISON_OP(name, comparison)                                        
      CASE(_if_acmp##name): {                                                
      int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1))          
        ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3;            
      address branch_pc = pc;                                            
      UPDATE_PC_AND_TOS(skip, -2);                                       
      DO_BACKEDGE_CHECKS(skip, branch_pc);                               
      CONTINUE;                                                          
    }

    STACK_OBJECT(-2) comparison STACK_OBJECT(-1)这一句即可看出比较的其实是操作数栈上两个对象在堆中的指针。

对于JVM有一定了解的同学,必然知道((x==y)=true)这个判断,如果xy的内存地址相同,那么意味着就是同一个对象,因此直接返回true

因此从上面的分析中,得到的结论是,==判断,比较的是两个对象的内存地址,如果==返回true,说明内存地址相同。

String.equals源码

继续分析equals中的源码,剩余部分源码的实现逻辑是

  1. 比较两个字符串的长度是否相等,如果不相等,直接返回false

  2. 把两个String类型转换为char[]数组,并且按照数组顺序逐步比较每一个char字符,如果不相等,同样返回false

public boolean equals(Object anObject) {
  //省略
  if (anObject instanceof String) { //判断对象实例是否是String
    String anotherString = (String)anObject; //强转成string类型
    int n = value.length;
    if (n == anotherString.value.length) { //如果两个字符串相等,那么它们的长度自然相等。
      //遍历两个比较的字符串,转换为char类型逐个进行比较。
      char v1[] = value;  
      c
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值