首先理解 ==
的概念,==
是 Java 语言中的比较运算符,用于比较两边的值是否相等。当对象使用 ==
比较时,比较的是对象的地址是否相等。
创建一个测试类:
public class MyData {
int a = 10;
}
比较两个对象是否相等:
@Test
void test() {
MyData data1 = new MyData();
MyData data2= new MyData();
System.out.println(data1 == data2); // false
System.out.println(data1.equals(data2)); // false
}
在 Java 中,所有的类都默认继承 Object
类,在源码中有介绍:
Object
类的 equals
方法默认使用 ==
进行比较,所以如果没有重写 equals
方法的话,默认使用 ==
进行比较:
public boolean equals(Object obj) {
return (this == obj);
}
上面的例子中,我们重写 MyData
类的 equals
方法:
public class MyData {
int a = 10;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MyData) {
MyData myData = (MyData) obj;
return myData.a == this.a;
}
return false;
}
}
输出比较结果:
@Test
void test() {
MyData data1 = new MyData();
MyData data2= new MyData();
System.out.println(data1 == data2); // false
System.out.println(data1.equals(data2)); // true
}
==
比较的是对象的地址,而 equals
方法默认使用 ==
作为比较运算符,但是可以重写 equals
方法,自定义比较规则。
==
常用于比较原生类型==
返回 true,而 equals
方法可以返回 true 或者 false 取决于重写方法的实现逻辑。通过上面的例子,我们知道包装类型使用 ==
比较的是引用地址,String
作为一个特殊的包装类型,由于 JVM 提供了常量池,所以需要单独考虑:
@Test
void testBaseType() {
String s1 = "dev";
String s2 = "dev";
System.out.println(s1 == s2); // true
}
在上面的代码中,由于定义 s1 的时候,会把值给存放到常量池里面;当定义 s2 的时候,会先去常量池里面找 123
这个值是否已经定义过,如果存在会把引用地址赋值给 s2,所以这里 s1 的引用地址等于 s2 的引用地址。
当使用构造函数创建 String
对象的时候:
@Test
void testBaseType() {
String s1 = "dev";
String s2 = "dev";
String s3 = new String("dev");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s3)); // true
}
使用构造函数创建的 String
对象是直接在堆中创建的,引用地址不一样,所以 ==
比较的结果为 false。而 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;
}
可以看出,重写后的 equals
方法比较的是字符串的 值
,所以 s1.equals(s3)
结果为 true。
首先来看一下 Integer 类型的比较:
@Test
void testInteger() {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = new Integer(100);
System.out.println(i1 == i2); // true
System.out.println(i1 == i3); // false
System.out.println(i1.equals(i3)); // true
Integer i4 = 128;
Integer i5 = 128;
System.out.println(i4 == i5); // false
System.out.println(i4.equals(i5)); // true
}
为什么会出现这种情况呢?首先,我们要知道:Integer i4 = 128;
这种定义方式会调用 Integer
的 valueOf()
方法。查看 valueOf
方法的源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
// 实例化并缓存起来了
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
可以看到,这里使用了内部类 IntegerCache
将 -128~127(最大值可自定义)
的整数都给提前实例化了,所以无论创建多少个这个范围内的 Integer
对象,都是同一个对象。
再来看一下 Integer
重写的 equals
方法:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
可以看到 Integer 之间的比较,是调用 Integer.ineValue()
获取 int 值然后使用 ==
比较的。
而 int 和 Integer 之间的比较,Integer 类型会自动拆箱成 int 类型,所以可以直接使用 ==
进行比较:
Integer i5 = 128;
int i6 = 128;
System.out.println(i5 == i6); // true
Long 和 Short 类型也做了相同的处理,只不过最大值是不可调的。
参考 Long 源码:
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
参考 Short 源码:
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
}