JavaSE易错知识点 String 的"equals"和"=="
对于很多初学java的同学来说,或许觉得String和int,float,这些没什么区别,可是在学习的过程中是否真的又明白呢,包括小菜在内也走了很多异想天开的弯路,总结出一些经验,所以在这里跟大家分享一下。首先,在不编译的情况的情况下看你能否答出以下三组问题的正确答案。
1 public StringDemo{ 2 public static void main(String[] args){ 3 StringDemo sd = StringDemo(); 4 sd.test1(); 5 sd.test2(); 6 sd.test3(); 7 } 8 9 void test1(){ 10 String a = "12345"; 11 String b = "12345"; 12 System.out.println(a == b); 13 System.out.println(a.equals(b)); 14 } 15 16 void test2(){ 17 String a = new String("12345"); 18 String b = new String("12345"); 19 System.out.println(a == b); 20 System.out.println(a.equals(b)); 21 } 22 23 void test3(){ 24 StringBuffer a = new StringBuffer("12345"); 25 StringBuffer b = new StringBuffer("12345"); 26 System.out.println(a == b); 27 System.out.println(a.equals(b)); 28 } 29 }
如果说你能很轻松的答出上面的结果,那么证明你是起码知道这三者之间的区别,分清了基本数据类型和引用数据类型。
最后的答案是:
test1:true true
test2: false true
test3: false false
首先,我们需要知道的是String这个类型在java中是一个特殊的类型,它既可以作为一种基本的数据类型又同时是一种引用类型,就如同int对应Integer一样,int是基本的数据类型,而Integer需要被实例化也就是new, String对应的就是String(在C#里面,为了区分,一个是大写一个是小写)。
在test1中,String a="12345"和String b="12345"实际上是存放在栈空间里的,而栈空间里的数据是可以共享的,因此,当声明String a = "12345"后它会首先在栈空间内去寻找有没有“12345”这个值,没有的话,就会创建一个存放在栈内的变量值为“12345”,而再次声明b的时候,它也会去栈空间里寻找有没有“12345”,此时栈空间内已经存在这个变量了,因此,b也指向栈里面的同一块变量,所以,a和b指向同一个变量,那么a=b?很显然是相等的,这就和 int a = 1,int b =1, a = b的原理是一样的,但仔细的朋友会发现,为什么int不可以equals呢?那时因为基本数据类型是没这个方法的,在test1中,当a调用equals时,此时的String又被当作了引用类型,所以是可以调用的,所有的类继承于Obejct类,而equals正是继承下来的方法,在equals的原始定义中是比较两个对象是否相同(详参API文档),而String则重写了这个方法(详参API文档),只要对象中的字符串相同即为相同,就好比Object new了两个中国人,可这两个中国人是一个人吗?而String new了两个中国人,并且规定只要国籍是中国的,我就视作为同一个人,因此,a和b同时指向同一个对象,他们的字符串内容必然是相同的,所以a.equals(b)也是true.
在test2中,a和b被作为引用类型各自new了一个实例,此时a和b各自指向的是堆空间里生成的两个实例,虽然他们包含的内容是相同的,可对于对象本身而言,他们是存放在不同的位置,所以a = b的结果是false,而对于equals而言,上一段中已经说明,由于String的equals方法是重写过的,所以只需要对象里的内容是否相同,在这里内容都是“12345”,很显然,结果就是true。.
在test3中,a和b都是StringBuffer类型的,这是一个引用类型,所以当被new过后堆空间里面生成了两块对象,a和b各自指向各自new的,所以和test2一样,a = b的结果是false,那么为什么他们相同的内容equals是flase,这又得回到第一段里说的,因为所有类都继承于Object,因此也就集成了equals方法,而StringBuffer这个类并没有重写也不能重写equals这个方法,也就是说他用的判定准则和Object是一样的,也是说他并不会像String一样把两个国籍是中国的人认为是同一个人,所以它的结果是false,为什么我要说是不能重写呢,因为通过API文档你可以看到StringBuffer的定义是finall的,也就是这个类既不能被修改也不能被继承,所以你无法重写或者通过集成重写他的equals方法。
不知道我的讲述大家看懂没有,下面留两个个小例子,请有兴趣的朋友试着分析分析结果,可以直接拷贝下来编译下。
Demo1:
1 class Cat(){ 2 private String name; 3 private String color; 4 private int age; 5 6 public Cat(String name,Sting color,int age){ 7 this.name = name; 8 this.color = color; 9 this.age = age; 10 } 11 12 //重写equals方法,大家可以网上搜如何重写 13 public boolean equals(Object obj){ 14 if(obj instanceof Cat){ 15 Cat cat = (Cat)obj; 16 return this.name.equals(cat.name)&& 17 this.color.equals(cat.color)&& 18 this.age == cat.age; 19 }else{ 20 return super.equals(obj); 21 } 22 } 23 24 //重写equals方法必须重写hashCode方法 25 public int hashCode(){ 26 return this.name.hashCode(); 27 } 28 } 29 30 31 public class Demo1(){ 32 public static void main(String[] args){ 33 Cat cat1 = new Cat("Tom","red",10); 34 Cat cat2 = new Cat("Tom","red",10); 35 System.out.println(cat1.equals(cat2)); 36 } 37 }
Demo2:
1 class Cat(){ 2 private String name; 3 private String color; 4 private int age; 5 6 public Cat(String name,Sting color,int age){ 7 this.name = name; 8 this.color = color; 9 this.age = age; 10 } 11 12 //重写equals方法,大家可以网上搜如何重写 13 public boolean equals(Object obj){ 14 if(obj instanceof Cat){ 15 Cat cat = (Cat)obj; 16 return this.name == cat.name&& 17 this.color == cat.color&& 18 this.age == cat.age; 19 }else{ 20 return super.equals(obj); 21 } 22 } 23 24 //重写equals方法必须重写hashCode方法 25 public int hashCode(){ 26 return this.name.hashCode(); 27 } 28 } 29 30 31 public class Demo2(){ 32 public static void main(String[] args){ 33 Cat cat1 = new Cat("Tom","red",10); 34 Cat cat2 = new Cat("Tom","red",10); 35 Cat cat3 = new Cat(new String("Tom"),"red",10); 36 Cat cat4 = new Cat(new String("Tom"),"red",10); 37 System.out.println(cat1.equals(cat2)); 38 System.out.println(cat3.equals(cat4)); 39 } 40 }