一、关于String类型的==
==是直接比较的两个对象的堆内存地址,如果相等,则说明这两个引用实际是指向同一个对象地址的。但是当String类型的对象使用==来比较的时候,又会是我们产生一些疑惑。话不多说,先看一下例子:
String s1 = "123";
String s2 = "123";
String s3 = new String("123");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false
System.out.println(s1.equals(s3)); //false
这个时候就有点懵逼了,不是说==是比较两个对象在堆内存的地址吗?难道说s1和s2这两个引用指向的是同一个对象吗?
那是因为,对于基本数据类型(byte,short,char,int,float,double,long,boolean)来说,他们是作为常量在方法区中的常量池里面以HashSet策略存储起来的。对于基本数据的包装类型(Byte, Short, Character,Integer,Float, Double,Long, Boolean)除了Float和Double之外,其他的六种也都是实现了常量池的,对于String s1="123"这种赋值形式,在编译器中实际上的操作是:(1)检查常量池中有没有“123”字符串,如果没有则生成一个“123”;(2)生成一个局部变量引用s1(或者s2)保存在栈中;(3)将引用指向常量池中”123“的地址。
在常量池中,一个常量只会对应一个地址,因此不管是再多的 “123”,"123" 这样的数据都只会存储一个地址,所以s1和s2都是指向的同一块地址,因此s1==s2的结果为true。
而对于String s3 = new String("123"),编译器实际操作是:(1)在堆中new一个新的String对象,这个String对象的value是“123”;(2)生成一个局部变量引用s3保存在栈中;(3)将引用指向堆中的String对象。显而易见,s1==s3的结果为false。
综上所述,为了避免出错,String对象的比较建议使用Strings类重写的equals方法。
二、关于Integer类型的==和equals
那么对于Integer类型,是不是也像String类型一样,所有都能用equlas方法来比较呢?答案是:不一定。
因为String继承了Object类并重写了equals方法,String类型的value值实际上是char[]数组,Strin类型的equlas方法实际上是对char[]数组的每一个值都进行了比较。而object类的equals方法仅仅只是比较两个引用指向的对象,实质上和==是等价的,以下是源代码:
那么对于以下例子;
Integer i1 = 127;
Integer i2 = 127;
Integer i3 = new Integer(127);
Integer i4 = 128;
Integer i5 = 128;
System.out.println(i1 == i2); //true
System.out.println(i1.equals(i2)); //false
System.out.println(i1 == i3); //false
System.out.println(i4 == i5); //false