equals 和 == 到底有什么区别?

Java 中的 equals== 对于不同的数据类型有不同的表现,话不多少,看演示。

对于基本数据类型

对于基本数据类型来说,只能用 ==,所以毫无疑问,这里是用来判断两个基本数据类型的值是否一致。

对于基本数据类型包装类

先来看看这种创建对象的方式:

1
2
3
4
5
6
7
8
public class Demo {
public static void main(String[] args) {
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
System.out.println(i1 == i2);
System.out.println(i1.equals(i2));
}
}

运行结果:

false
true

这里可能会有人疑问,== 不是判断两者是否相等么,那为什么结果为 false 呢?

因为这里的 Integer 是一个对象,也就是引用数据类型,里面存放的是对象在堆内存中的引用值,所以这里的 == 只是判断两者的引用值是否相同,两者创建了两个对象,在堆内存中分别是不同的引用,由而引用值也不同,才会返回 false

那么我们再看一下简化版的 Integer 的创建方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package other;

public class Demo {
public static void main(String[] args) {

Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2);
System.out.println(i1.equals(i2));

Integer i3 = 1000;
Integer i4 = 1000;
System.out.println(i3 == i4);
System.out.println(i3.equals(i4));
}
}

运行结果:

true
true
false
true

equals 毫无疑问是判断值是否相同,所以结果为 true,但是为什么第一个 == 又为 true 了呢,而第二个 == 还是 false。

因为在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。但适用范围只有 -128+127 也就是说当自动装箱时,不会再分配内存空间,而是使用缓存中已存在的对象。

最大值 127 可以通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改

这种缓存行为不仅适用于 Integer 对象。我们针对所有整数类型的类都有类似的缓存机制。ByteShortLongCharacter 都支持缓存。

其中 ByteShortLong 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。

对于基本数据类型包装类而言,不论是使用哪种方式创建的,判断两者的值是否相同,务必要使用 equals ,而不要使用 ==

对于自定义数据类型

首先有 Person 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Person {
private int id;
private String name;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

然后示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setId(1);
p1.setName("张三");

Person p2 = new Person();
p2.setId(1);
p2.setName("张三");

System.out.println(p1 == p2);
System.out.println(p1.equals(p2));
}
}

这里使用 == 的结果为 true 很容易理解,毕竟两个对象的引用不同,但是 equals 不是判断值是否相同么,我的两个idname 都是一样的,为什么这里还会返回 false 呢?

Person 中我们没有写 equals 方法,根据 Java 继承的特点,Person 类继承的是 Object,所以使用的是 Object 的 equals 方法,那我们来看看 Object 的 equals 方法是怎么写的。

![image_1bo22o7qv1e801ivjhqftge18d1m.png-24.9kB][1]

原来这里的 equals 方法里用的也是 ==,那么刚才我们用基本数据类型包装类时,为什么能判断呢,同样,我们找到 Integer 的 equals 方法。

![image_1bo22v5je88n1d611g3la3e1a1m13.png-33kB][2]
![image_1bo2315q517g21glb14ni11c9hdp2n.png-18.8kB][3]

可以看 Integer 的 equals 方法,会先判断该对象是否是一个 Integer 类的一个实例,如果不是则直接返回 false,是的话就比较他们的 value 值是否相等。

所以在我们自定义数据类型时,比较两个对象是否相同,一定要重写 equals 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean equals(Object obj) {

if (obj instanceof Person) {

Person p = (Person)obj;

if (this.id == p.id && this.name == p.name) {
return true;
}
}
return false;
}

重写 equals 方法后的运行结果:

false
true

对于 String

String 也有两种创建方式,一种是 String str = new String("java");,另一种是 String str = "java"

Java 为 String 类型提供了缓冲机制,当使用第二种双引号的形式创建对象时,Java 会先去字符串缓冲池中寻找内容相同的字符串,如果存在就直接拿来应用,如果不存在就创建一个新的字符串放到缓冲池中。对于第一种 new 对象的形式而言,每次都会创建一个新的对象,不会访问字符串缓冲池。

既然如此,对于 String 的 equals== 你也自然应该明白是什么结果了。

一般情况下,建议使用 String s = "abc" 方式,因为该方式采用的是字符串缓冲池机制,效率更高。

结论

在引用类型中,”==” 是比较两个引用是否指向堆内存里的同一个地址(同一个对象),而 equals 是一个普通的方法,该方法返回的结果依赖于自身的实现。

对于引用类型而言,判断两者是否相同要用 equals 方法,而不是 ==。当然对于自定义数据类型,记得要重写 equals 方法,不然效果就等同于 == 了。
[1]: https://cdn.jun6.net/image_1bo22o7qv1e801ivjhqftge18d1m.png
[2]: https://cdn.jun6.net/image_1bo22v5je88n1d611g3la3e1a1m13.png
[3]: https://cdn.jun6.net/image_1bo2315q517g21glb14ni11c9hdp2n.png