Java中==和equals有什么区别

61 阅读3分钟

前言

这是一个比较老生常谈的问题了,先说结论:

  • equlas默认比较的是地址值。因为Object类是所有类的父类,如果当前类重写了Object的equals方法,则按照重写的规则去处理
  • ==的情况,如果是基本数据类型比较的是真实值,如果是引用数据类型,比较的是地址值

==

Java中定义了八种基本数据类型,依次是byte,short,int,long,boolean,float,double,char;以及引用数据类型,例如String

先看代码

public static void main(String[] args) {  
    int i = 1;  
    int j = 1;  
  
    String x = new String("hello word");  
    String y = new String("hello word");  
  
    System.out.println(i == j);  
    System.out.println(x == y);  
}

结果分别是true和false; 上述结果证明了,针对==的结论是正确

但在编码过程中,实际申明String的过程如下:

public static void main(String[] args) {  
    String x = "hello word";  
    String y = "hello word";  
  
    System.out.println(x == y);  
}

结果是true

因为在JVM中,申明的字符串是存储在字符串常量池中,当声明y变量时,JVM会发现常量池中已经存在了hello word了,直接引用了该字符串;所以导致了结果是true,但实际上比较的还是地址值

equals

先看Object的equals方法

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

Object类中的equals方法,实际上调用的==

再看一下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;  
}

String类中先进行了==(引用类型进行地址值比较),如果引用的是同一个字符串,则返回true。否则进行值比较

再看一下Integer类的equals方法

public boolean equals(Object obj) {  
    if (obj instanceof Integer) {  
        return value == ((Integer)obj).intValue();  
    }  
    return false;  
}

Integer类中实际采用了值比较(将Integer引用类型转成了int类型)

综述,equals,如果当前类没有重写equals方法,比较的是地址值;如果重写了该方法,则按照重写规则比较

题外话

看如下代码:

public static void main(String[] args) {  
    Integer i = 1;  
    Integer j = 1;  
  
    Integer x = 129;  
    Integer y = 129;  
    System.out.println(i == j);  
    System.out.println(x == y);  
}

上述代码执行的结果是true,flase 根据之前的结论,==在引用数据类型,比较的是地址值;那为什么会造成这种结果?

Java有一种存在叫做自动装箱,通过javap查看字节码可以得到如下内容

在申明Integer i = 1时,执行了Integer.valueOf方法,这就是自动装箱

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

Integer类中配置了一个静态缓存数组(也就是上面的IntegerCache),存储了-127 ~ 128内的数字; 如果在执行valueOf方法时,发现申明的数值在-127 ~ 128范围内,直接返回缓存中的对象 如果不是,则返回一个新的Integer对象。

也就是说,在上面的代码中,==针对Integer依旧比较的是地址值,只是数值在-127~128以内,返回的是Integer类缓存的对象。所以会导致i == j 是true。

这种现象不仅限于Integer类,包括:

类型缓存范围说明
Byte-128 到 127-
Short-128 到 127-
Integer-128 到 127-
Long-128 到 127-
Character0 到 127ASCII字符
Booleantrue/false
String常量池编译时确定的字符串