一、integer的==
public class Test03 {
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System. out.println( f1 == f2); //true
System. out.println( f3 == f4); //false
}
}
当我们给一个 Integer 赋予一个 int 类型的时候会调用 Integer 的静态方法 valueOf。
Integer f1 = Integer.valueOf(100);
Integer f2 = Integer.valueOf(100);
Integer f3 = Integer.valueOf(150);
Integer f4 = Integer.valueOf(150);
思考:那么 Integer.valueOf()返回的 Integer 是不是是重新 new Integer (num); 来创建的呢?如果是这样的话,那么 == 比较返回都是 false,因为他们引用的堆地址不一样。
具体来看看 Integer.valueOf 的源码
public static Integer valueOf(int i) {
//范围-128到127
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
从上面我们可以知道给 Interger 赋予的 int 数值在 - 128 - 127 的时候,直接从 cache 中获取,这些 cache 引用对 Integer 对象地址是不变的,但是不在这个范围内的数字,则 new Integer (i) 这个地址是新的地址,不可能一样的.
二、string的==
String str=new String ("abc") 和 String str="abc" 的字符串 “abc” 都是存放在堆中,而不是存在栈中。
其实在在 java 中有一个 “字符数据池” 的内存管理机制。
String str="abc",执行这句话时,会先去 “字符数据池” 搜索时候有 “abc” 这个字符串,如果有,则将字符串的首地址赋值给 str,如果没有,生成一个新的字符串 “abc” 并且将首地址赋值给 str;
String str=new String ("abc"),执行这句话时,不会考虑时候已经存在了 “abc” 这个字符串,而是直接生成一个新的字符串 “abc” 并将首地址赋值给 str,如果池中没有字符串,则把这个"abc"放入字符串池中(此时是创建了两个对象,一个堆中,一个池中);
由以上分析可知,String str="abc" 和效率要高于 String str=new String ("abc"),因为如果有重复的字符串时,第一种方式可以节省空间。
下面举例说明一下,好好看看结果,仔细分析原因,上面已经说明的很清楚了:
public class Test{
public static void main(String args[]){
String s1=new String ("abc");// 直接在堆中生成新的 “abc”
String s2=new String ("abc");// 直接在堆中生成新的 “abc”
String s3="abc";// 先去 “字符数据池” 搜索时候有 “abc” 这个字符串,如果有,则将字符串的首地址赋值给 s3,如果没有,则在 “字符数据池” 中生成一个新的字符串 “abc” 并且将首地址赋值给 s3;
String s4="abc";// 去 “字符数据池” 搜索时发现了上一步生成的 “abc” 这个字符串,把该字符串首地址赋值给 s4,这时其实 s3 和 s4 指向同一个字符数据池中的 “abc”
System.out.println(s1==s2);
System.out.println(s1==s3);
System.out.println(s2==s3);
System.out.println(s3==s4);
}
}
结果:
false
fasle
false
true
三、基本类型和包装类的==和equals(涉及拆装箱)
1、基本型和基本型的封装型进行 "= =" 运算符的比较,基本型的封装型将会自动拆箱变为基本型后再进行比较,因此 Integer (0) 会自动拆箱为 int 类型再进行比较,显然返回 true;
2、两个 Integer 类型进行 "= =" 比较,如果其值在 - 128 至 127,那么返回 true,否则返回 false, 这跟 Integer.valueOf () 的缓冲对象有关,这里不进行赘述。
3、两个基本型的封装型进行 equals () 比较,首先 equals () 会比较类型,如果类型相同,则继续比较值,如果值也相同,返回 true
4、基本型封装类型调用 equals (), 但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行 3 中的比较。
5、对于equal方法来说,如果没有重写过,那么就是相当于==,equals方法是基类Object的方法,所有对象都可以调用这个方法,而equals底层实现就是==,也就是直接比较地址的,所以呢,如果没有重写equal方法的对象调用equal方法,是直接比较对象地址的。
四、关于equal和 == 的总结
'==' 是用来比较两个变量(基本类型和对象类型)的值是否相等的, 如果两个变量是基本类型的,那很容易,直接比较值就可以了。如果两个变量是对象类型的,那么它还是比较值,只是它比较的是这两个对象在栈中的引用(即地址)。
对象是放在堆中的,栈中存放的是对象的引用(地址),由此可见 '==' 是对栈中的值进行比较的。如果要比较堆中对象的内容是否相同,那么就要重写 equals 方法了。
Object 类中的 equals 方法就是用 '==' 来比较的,所以如果没有重写 equals 方法(string是重写了equal方法的),equals 和 == 是等价的。 通常我们会重写 equals 方法,让 equals 比较两个对象的内容,而不是比较对象的引用(地址)因为往往我们觉得比较对象的内容是否相同比比较对象的引用(地址)更有意义。
Object 类中的 hashCode 是返回对象在内存中地址转换成的一个 int 值(可以就当做地址看)。所以如果没有重写 hashCode 方法,任何对象的 hashCode 都是不相等的。通常在集合类的时候需要重写 hashCode 方法和 equals 方法,因为如果需要给集合类(比如:HashSet)添加对象,那么在添加之前需要查看给集合里是否已经有了该对象,比较好的方式就是用 hashCode。
注意的是 String、Integer、Boolean、Double 等这些类都重写了 equals 和 hashCode 方法,这两个方法是根据对象的内容来比较和计算 hashCode 的。(详细可以查看 jdk 下的 String.java 源代码),所以只要对象的基本类型值相同,那么 hashcode 就一定相同。