Java中的equals和==有什么不同

今天看到两道题:

  • equals==有什么区别?

  • Integer a = 1;
    Integer b = 1;
    Integer a1 = 10000;
    Integer b1 = 10000;
    
    System.out.println(a == b); // true
    System.out.println(a1 == b1); //false
    

突然感觉对这一块的基础知识较为模糊,所以专门整理下这里的知识点,对JVM的操作数栈和字节码指令信息比较熟悉更容易理解下面的内容。

1. ==

网上的资料的解释:==针对基础数据类型是直接比较值,而针对引用数据类型则是比较引用地址是否相同,题目2中用到的都是引用数据类型,为什么还会产生这样的区别呢?

JAVA代码编译后会形成字节码指令,所以可以先通过javap -v来看一看能不能从字节码中看出问题所在:

整个程序的字节码指令过程如下:

  • 加载操作数到操作数栈
  • 通过Integer.valueOf()方法自动装箱操作
  • 使用if_acmpne比较栈顶两个对象的引用是否相同

可以看到,两段程序除了加载操作数到操作数栈所使用的指令不同(JVM对于取值**-15**采用`iconst`指令,取值**-128127采用bipush指令,取值-3276832767**采用`sipush`指令,取值**-21474836482147483647**采用 ldc 指令)外,其他的操作完全相同,所以唯一能够产生不同的地方就在于自动装箱操作了。

果然在Integer的源码中发现了原因:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

该方法会对-128~127的数据进行缓存,对于这一区间的数据返回的都是同一个对象,才导致了上面的不同。

扩展一下:

int a = 10000;
int b = 10000;
Integer c = 10000;

System.out.println(a == b);
System.out.println(a == c);

Map<Integer, Integer> map = new HashMap<>();
map.put(new Integer(10000), 1);
System.out.println(map.containsKey(new Integer(10000)));

还是通过javap -v来看一看字节码

可以看到对于int直接的比较使用了if_icmpe指令对操作数直接进行比较,而对于intInteger直接的比较则是通过对Integer调用Integer.intValue()自动拆箱后再调用if_icmpe指令,所以前两个结果都应该是true。
对于最后一个结果也是true,因为HashMap的containsKey()是通过对象的hashCode判断的,而Integer对象的hashCode就是value本身。

2. equals()

上面提到==仅可用于基本数据类型的值比较,如果想要对引用类型的值进行比较怎么办呢,可以使用equals(),但是需要注意所使用的类型一定要覆写了Object类中的equals方法才能实现值比较

Object类中的equals方法:

public boolean equals(Object obj) {
    return (this == obj);
}

String中的equals方法:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}