==运算符 的那些坑
Java中的数据类型分为两大类:
- 基本数据类型(boolean、byte、short、char、int、long、float、double)。八种基本数据类型中的数字类型实际上是存储的一组位数不同的二进制数。boolean存储的是true和false两种
- 引用类型(String等),存储的是其所指向对象在内存中的内存地址,内存地址也是一串二进制数。
- ==在比较基本数据类型时,比较的是他们的值;在比较引用类型时比较的是他们的内存地址
那么问题来了,当int与Integer比较的时候,比较的是什么呢?
首先区别一下int和Integer的区别:
int是基本数据类型,默认的初始值为0
- Integer是引用类型,默认的初始值为null
好,先不说==比较的结果,上代码。
package equalstest;
public class test {
public static void main(String[] args){
int a = 127;
int b = 127;
System.out.println("a==b: " + (a == b)); //1.true
Integer x127 = 127;
Integer y127 = 127;
System.out.println("x127==y127:" + (x127==y127)); //2.true
System.out.println("x127==a:" + (x127==a)); //3.true
Integer x128 = 128;
Integer y128 = 128;
int c = 128;
System.out.println("x128==y128:" + (x128==y128)); //4.false
System.out.println("x128==c:" + (x128==c)); //5.true
Integer newx127 = new Integer(127);
System.out.println("newx127==x127" + (newx127 == x127)); //6.false
Integer newx = new Integer(127);
Integer newy = new Integer(127);
System.out.println("newx == newy" + (newx == newy)); //7.false
}
}
第一个true:比较两个基本数据类型int类型的变量,==在比较基本数据类型时,比较的是值是否相等。
第二个true:比较两个包装类Integer的对象,对象中存放的值相等且位于-128–127之间的数,比较的结果为true。真神奇!原因解释如下:
java编译 Integer x127 = 127 这条语句时,是将其转变为 Integer x127 = Integer.valueOf(127),即先拆箱再比较,那我们先看看jdk提供的api中Integer类的源码。
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 {
private IntegerCache() {}
static final Integer cache[] = new Integer[-(-128) +127 + 1];
static {
for (int i = 0; i < cache.length; i++) {
cache = new Integer(i - 128);
}
}
}
jdk中的源码中显示:对于传入的参数数值在-128到127之间的数,会进行缓存,将其存到栈内存中,如果下次再有 Integer y127 = 127; 语句时,会从内存中去出,不会重新new一个新的对象,因此该句的结果是true。OK,问题解决了!!!从IntegerCache 类中可以看出:Integer已经默认创建了数值在-128到127的缓存数组,当需要-128到127之间的数的时候,JVM会直接在该对象池中找到该值的引用
第三个true和第五个true:只要Integer和int类型的数据进行比较,都可以理解为是值的比较。因为Integer通过拆箱的方式与int的变量进行比较
第四个false:超过127后,JVM会为每一个Integer申请新的内存空间
第六个false:因为newx127是通过new的方式得到的内存,所以返回false
第七个false:两个变量都是通过new的方式,申请了两块内存
equals()方法
Java当中所有的类都是继承Object这个基类,在Object类中定义了equals()方法,源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看出Object类中使用==比较的是对象的内存地址是否相等,但是很多类库中的这个方法都被类所覆盖了,如String,Integer等类,他们的equals方法已经不是比较内存地址是否相等了,大多数比较的是对应的值是否相同。其中String类中的equals源码如下:
public boolean equals(Object anObject) {
//首先判断两个对象的内存地址是否相同,如果相同,直接返回true
if (this == anObject) {
return true;
}
//判断传递进来的参数类型是否是要比较的String类型
if (anObject instanceof String) {
//将传递进来的参数强制转换为String,其实能进到if里面的一定就是String类型
String anotherString = (String)anObject;
//count应该是String类前面有得到的该对象的长度,到底是不是我没有去前面找。因为懒~
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
//比较两个字符数组中的值是否相等
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
覆写equals()方法的一些建议
- 显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量
- 检测this与otherObject是否引用同一个对象
if (this == otherObject ) return true;
- 这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。
- 检测otherObject是都是null,如果是null,返回false。这项检查是有必要的
if (otherObject == null) return true;
- 比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有多改变,就是用getClass检测:
if(getClass()!= otherObject.getClass()) return false;
- 如果所有的子类都拥有同意的语义,就是用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
- 将otherObject转换为相应的类类型变量
ClassName other= (ClassName)otherObject
- 现在开始对所有需要比较的域进行比较。使用==比较基本数据类型,使用equals比较对象域。如果所有的域都匹配成功,就返回true,否则返回false;如果在子类中重新定义了equals,就要在其中包含调用super.equals(other)