Java基础之==与equals

1. 使用 ==

1.1 基本类型比较

int a = 1;
int b = 1;
byte c = 1;
Integer d1 = new Integer(1);
Integer d2 = new Integer(1);
System.out.println(a == b); 
//结果:true
System.out.println(a == c); 
//结果:true
System.out.println(a == d1); 
//结果:true
System.out.println(d2 == a); 
//结果:true
System.out.println(d1 == d2); 
//结果:false

java中的基本类型,包含:int、long、short、byte、char、boolean、float、double这8种,可以使用==号判断值是否相等。

如果出现了基本类型的包装类,比如:Integer,用一个基本类型和一个包装类,使用号也能正确判断(a == d1),返回true,因为Integer和int比较时,会自动拆箱,这是比较值是否相等。

但如果有两个包装类(d1 == d2),使用==号判断的结果可能是false,因为两个Integer比较时,比较的是它们指向的引用(即内存地址)是否相等。

Integer d3 = 1;
Integer d4 = 1;
Integer d5 = 128;
Integer d6 = 128;
System.out.println(d3 == d4); 
//结果:true
System.out.println(d5 == d6); 
//结果:false

为什么结果不一样呢
答:因为Integer有一个常量池,-128~127直接的Integer数据直接缓存进入常量池。所以1在常量池,而128不在。

new的Integer对象不适用常量池(如d1 == d2)

1.2 字符串比较

String e = "abc";
String f = "abc";
String g = new String("abc");
String h = new String("abc");
System.out.println(e == f); 
//结果:true
System.out.println(e == g); 
//结果:false
System.out.println(g == h); 
//结果:false

普通的字符串变量,使用==号判断,也能返回正确的结果。

但如果一个普通的字符串变量,跟new出来的字符串对象使用 == 号判断时,返回false。这一点,跟之前说过的用一个基本类型和一个包装类,使用 == 号判断的结果有区别,字符串没有自动拆箱的功能,这一点需要特别注意。

此外,两个new出来的字符串对象使用 == 号判断时,也返回false,因为他们比较比较的两个对象的内存地址,而不是内存地址所存放的值。

总结:使用 == 号,可以非常快速判断8种基本数据类型是否相等,除此之外,还能判断两个对象的引用是否相等。

2. 使用equals方法

那怎么判断两个new对象的值是否相等呢?

答:使用equals方法。

equals方法其实是Object类中的方法:只判断两个对象的引用是否相等。

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

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;
}

它依然会先判断两个对象引用是否相等,如果相等返回true。接下来,会把两个字符串的挨个字符进行比较,只有所有字符都相等才返回true。

String g = new String("abc");
String h = new String("abc");
System.out.println(g.equals(h));
//结果:true

3. 空指针异常

前两种判断,都可能会报空指针异常。

3.1 ==

int a = 1;
Integer b = new Integer(1);
Integer c = null;
System.out.println(a == b); 
//结果:true
System.out.println(a == c); 
//结果:NullPointerException

int和Integer使用==号判断是否相等时,Integer会自动拆箱成int。

但由于c在自动拆箱的过程中,需要给它赋值int的默认值0。而给空对象,赋值0,必然会报空指针异常

3.2 equals

String e = null;
String f = "abc";
System.out.println(e.equals(f)); 
//结果:NullPointerException
System.out.println(f.equals(e));
//false

为什么一个报空指针另一个没有呢?

我们先看源码:

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

Soga

按照equals的逻辑,只把前者为空的情况考虑进去了,这就解释了为什么有两种不同的结果

等等 这里还有个问题,在equals方法里面也使用了 == 为什么不会报空指针异常呢?

答:因为而Objects类的equals方法,使用了Object类型接收参数,它的默认值是null,不用进行类型转换,也不用像int类型对象赋值默认值0。

3.3 解决办法

重写equals:在代码中判空

private static boolean equals(String e, String f) {
    if (e == null) {
        return f == null;
    }
    return e.equals(f);
}

4. Object.equals的坑

Integer a = 1;
Integer b = 1;
long c = 1L;
System.out.println(Objects.equals(a, b));
//结果:true
System.out.println(Objects.equals(a, c));
//结果:false

equals(a,c)为什么是false呢?为什么呢?为什么呢?

答:这就要从Integer的equals方法说起了。

它的equals方法具体代码如下:

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

先判断参数obj是否是Integer类型,如果不是,则直接返回false。如果是Integer类型,再进一步判断int值是否相等。

而上面这个例子中c是long类型,所以Integer的equals方法直接返回了false。

也就是说,如果调用了Integer的equals方法,必须要求入参也是Integer类型,否则该方法会直接返回false。

原来坑在这里!!!

除此之外,还有Byte、Short、Double、Float、Boolean和Character也有类似的equals方法判断逻辑。即同类型的变量判断等同于==,但是不同类型的就…

那么,如何解决上面的问题呢?

解决1: 强制类型转换

System.out.println(Objects.equals(a, (int)c));
//结果:true
System.out.println(Objects.equals(c, (long)a));
//结果:true

解决2: 用==

System.out.println(a == c);
//结果:true