简单一句话:==表示变量块中的内容相等,equals表示属性相等。
为什么要强调变量块中的内容?其实这里所说变量块中的内容相等其实就是变量相等,但是为了说清楚==,这么说可以更好地区别基本数据类型和引用的情况。
变量块中的内容,无非两种情况,基本数据类型 或者 地址(对对象的引用)。==就是比较这其中的内容是否一致。
Object obj_1 = new Object();
Object obj_2 = new Object();
这就不难理解为什么 obj_1==obj_2 为false了。因为new出来的两个Object在堆中为两个对象,所以obj_1和obj_2中存的地址不一样。
但是基本数据类型不存在这个问题,对于基本数据类型都是直接拿出来,比较值是否相同。
从根本上来说,对于基本数据类型和对象的==没有区别,都是拿变量存的值来进行比较,只要一致那就返回true,不一致就返回false。所以如果变量引用相同的对象,那么其值也相同,返回也为true。
注意==的坑
主要是一些对象创建时会直接返回常量池地址(String)或者堆中已缓存对象(Integer)。
String:
String str_1 = "abc";
String str_2 = "abc";
String str_3 = "ab" + "c";
String str_4 = new String("abc");
str_1=str_2=str_3≠str_4
str_1=str_2都为字符串常量池(在堆中)“abc"的地址。在第一次定义时,常量池中没有"abc"为自动创建。
str_2=str_3是编译器有关,编译器会自动处理这种可处理的常量,在编译后"ab”+"c"和"abc"没有区别。
str_3≠str_4是用new在堆中开辟了一个新的对象。其实在javap后会发现,调用String构造函数的过程也是在字符串常量池中创建了新的字符串常量,然后String对象指向这个常量。相当于str_4->String->“abc”,所以此时str_4为这个String对象的地址。
Integer:
在方法区中,有一个静态类IntegerCache:
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
当int类型自动装箱时,会调用Integer的valueOf方法:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
可以看到valueOf在-128到127直接返回了方法区中的静态Integer。
所以如果通过自动装箱创建两个值相同的Integer,其==是相同的;但是如果通过new创建,就会不同。
Equals
其实equals只是一个类方法,由于java所有类都继承自Object,而Object中实现了equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
如果不重写类的equals方法,其实和用==没有任何区别。
所以判断内容是否相等最终都要看类的equals方法如何实现,java的api中基本都实现了equals方法。
可以使用lombok中的@EqualsAndHashCode注解来实现自动重写。
属性callSuper=true表示父类属性也要相等,但是会调用父类的equals方法判断,如果父类没有重写,还是调用==,为false。
2024.5.14补充
Integer==int会发生什么?
由于Integer有自动拆箱的特性,当用Integer和int比较时会自动转换成int,所以不存在上述问题。