Java学习笔记——equals与==比较
文章目录
首先我们先来思考一下以下程序运行的结果:
Integer i1 = 127;
Integer i2 = 127;
System.out.println("i1 == i2 :" + (i1 == i2));
System.out.println("i1.equals(i2) :" + i1.equals(i2));
Integer i3 = 128;
Integer i4 = 128;
System.out.println("i3 == i4 :" + (i3 == i4)); // 注意与i1 == i2的区别
System.out.println("i3.equals(i4) :" + i3.equals(i4));
int i5 = 128;
Integer i6 = 128;
System.out.println("i5 == i6 :" + (i5 == i6));
Integer I1 = new Integer(127);
Integer I2 = new Integer(127);
System.out.println("I1 == I2 :" + (I1 == I2));
System.out.println("I1.equals(I2) :" + I1.equals(I2));
System.out.println("i1 == I1 :" + (i1 == I1));
System.out.println("i1.equals(I2) :" + i1.equals(I2));
String s1 = "abc";
String s2 = "abc";
System.out.println("s1 == s2 :" + (s1 == s2));
System.out.println("s1.equals(s2) :" + s1.equals(s2));
String S1 = new String("abc");
String S2 = new String("abc");
System.out.println("S1 == S2 :" + (S1 == S2));
System.out.println("S1.equals(S2) :" + S1.equals(S2));
System.out.println("s1 == S1 :"+(s1 == S1));
System.out.println("s1.equals(S1) :" + s1.equals(S1));
执行结果为:
i1 == i2 :true
i1.equals(i2) :true
i3 == i4 :false
i3.equals(i4) :true
i5 == i6 :true
I1 == I2 :false
I1.equals(I2) :true
i1 == I1 :false
i1.equals(I2) :true
s1 == s2 :true
s1.equals(s2) :true
S1 == S2 :false
S1.equals(S2) :true
s1 == S1 :false
s1.equals(S1) :true
为什么会是这些结果呢,首先来了解一些相关Java
基础:
(一)对象的创建=
与new
- 形如
String s = "ABC";
s
会存放在栈中,s
指向常量池中"ABC"
的位置,即"ABC"
的地址。
"ABC"
是常量,创建这个常量的时候首先会判断常量池中是否存在该值,如果存在,则s
直接指向该值所在地址,否则会在常量池中新建一个值并指向它。图解如下:
再执行String str = "ABC";
后结果如下:
由此可知,两个通过=
直接赋值的对象,只要它们值一样,那么它们所指向的地址就一样。但是,这并不是绝对的,比如Integer i1 = 128
和Integer i2 = 128
,我会在后面提到这个。
- 形如
String s1 = new String("AAA");
所有通过new
出来的对象都会在堆中新开一个内存来存放这个对象,这个例子中,new String()
存放在堆中,并把这个对象地址空间的引用赋值给s1
,"AAA"
是常量,创建这个常量的时候首先会判断常量池中是否存在该值,如果存在,则new String()
直接指向该值所在地址,否则在常量池中新建一个值并指向它。图解如下:
由此可知,所有通过new
出来的对象,它们的地址都是不一样的。
(二)==
与equals
详解
(1)==
基本数据类型只能用==
进行比较,八大基本数据类型:char/byte/short/int/long/float/double
的比较都是用==
,比较的是它们的值。例如:
int a = 1;
int b = 1;
System.out.println(a == b); // true
对象如果用==
进行比较,则是比较它们的地址值,只要两个对象地址值不一样,结果就为false
。通过对第一部分的理解,很容易知道所有用new
创建的对象用==
比较结果都为false,用=
创建的对象用==
比较结果都为true。例如:
String s1 = "aaa";
String s2 = "aaa";
System.out.println(s1 == s2); // true
String s3 = new String("aaa");
String s4 = new String("aaa");
System.out.println(s3 == s4); // false
另外Java
中对Integer
有自动装箱拆箱的功能,Integer
对象和基本数据类型int
比较时,java
会自动拆包装为int
,然后进行比较,实际上就变为两个int
变量的比较。,以下结果为true
:
Integer I1 = new Integer(1);
int I2 = 1;
Integer I3 = 1;
System.out.println(I1 == I2); // true
System.out.println(I2 == I3); // true
(2)equals
equals()
方法是Object
类的,所有继承于它的类都可以重写这个方法,下面是Object
类中equals()
的源码,可以看出Object
中equals()
方法间接的用了==
来比较两个对象是否一样,也就是比较它们的地址。
public boolean equals(Object obj) {
return (this == obj);
}
我们日常用的String
、Integer
等类一般都重写了equals()
方法,下面是String
类的equals()
方法源码,String
类重写之后这个方法就是比较String
的值了,而不是比较地址。
public boolean equals(Object anObject) {
if (this == anObject) { // 如果他们地址相同,返回true
return true;
}
if (anObject instanceof String) { // 如果anObject是String类型
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) { // 如果长度相同再进一步判断,否则直接返回false
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;
}
下面是Integer
类的equals()
方法源码,重写equals()
之后也是比较值,而不是比较地址。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
通过以上分析,容易得出下面程序的结果:
Integer i1 = 128;
Integer i2 = 128;
Integer I1 = new Integer(128);
Integer I2 = new Integer(128);
System.out.println(i1.equals(i2)); // true
System.out.println(I1.equals(I2)); // true
System.out.println(i1.equals(I1)); // true
(三) Integer
对象的创建过程
Integer i = 100;
这样声明Integer
对象会调用Integer
的一个函数:ValueOf()
,对于Integer
,Java
提供了缓存机制(Byte、Short、Integer、Long、Character
都具有缓存机制),默认缓存数值为区间[-128, 127]
的Integer
对象,可以查看JDK
源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
其中默认low = -128
,high = 127
,这样,当再次创建值在[-128, 127]
区间的Integer
对象时,会复用缓存中的对象,也就是直接指向缓存中的Integer
对象,如果创建的对象数值不在区间[-128, 127]
,则会返回new Integer(i)
,即通过new
方式创建对象。
我们可以通过下面代码来验证:
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2); // true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); // false
因为i1
和i2
的数值都在区间[-128, 127]
中,所以i1
和i2
指向同一个地址,故==
的结果为true
。
而i3
和i4
的数值都不在区间[-128, 127]
中,所以都是通过new
方式创建的,故地址不同,故==
的结果为false
。
Integer i = new Integer(100);
机制与上面提到的new String("AAA")
是一致的,会在堆上创建Integer
对象,并把引用值赋值给i
。
通过以上的分析,文章开头那段程序为什么是那个结果应该就很简单了。