对于 equals与== 想必大家都非常熟悉,说开点,所有类的父类Object,里面就有equals 方法,里面的实现就是==。
不过,对象有三大特性,封装,继承,多态,许多类继承Object 类之后,根据需要,封装了不同的属性,自然也不能直接进行==,这样只是比较地址,毫无意义,于是各种子类都有了自己的实现,比如:Date类中的equals 比较的是时间,HashMap中Node<K,V>的equals 比较地址,同时也比较值,任意一个相同就返回true。
当然,我们平时遇到比较多的还是String中的equals和==。不过说句实在话,代码中谁用== 啊,都是用equals,没啥说的,安全!
让我们来看一下String中equals 的实现:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
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;
}
这个代码倒是没什么难度,就是先比较两个字符串是不是同一个,比较地址,然后就是比较String中value数组是否相同。两个条件只要有一个成立,equals就返回true。
这个其实大家都知道,写代码也不会在String中的equals这里出现bug,为啥这么多人都热衷于区分equals与==呢?面试经常问呗,不然谁去刨根问底啊,闲的慌啊?
最初级的菜鸟遇到的估计就是什么String中equals与==,有什么区别,可以互换么?问什么?源码中很容易看出来答案、
然后成长了一点,就来一个判断几个String 的== 是true还是false。
在之后,面试的开胃小菜中可能会碰到关于String存储之类的问题。
下面就来说一说String存储和相等的判断。
大家都知道JMM底层的存储结构,简单的分一下,就是堆内存,栈内存,方法区,常量池,寄存器之类的。今天我们针对的是String,所以我们主要研究的就是堆内存,栈内存,与常量池!
其实常量池也是在堆内存中开辟的一段区域,数据的存储主要还是在堆内存。
我们来看一下String创建存储的过程。
String str = new String("abc");
新建一个String对象,并完成赋值abc,那它经历了怎样的创建过程呢?
首先,这个操作肯定会生成一个“abc”的字符串。内存栈中新建一个str 作为对象的引用。
接着,就是堆内存,创建一个值是“abc”的String对象。最后,就是检查常量池是否有“abc”字符串,如果没有,那么要新创建一个,如果有,则忽略。
这是一个简单的创建过程,那好,现在我们看一下具体例子:
如上图,创建了5个对象,我们一个一个看。
第一个,创建了一个对象的引用s1,在常量池中创建了一个“abc”,堆内存中应该也创建了一个对象,不过没有引用,下次GC会被回收。
第二个,创建了一个对象的引用s2,堆中创建了一个对象,常量池中已经有了“abc”,不在创建。
第三个,创建了一个对象的引用s3,堆中创建了创建了“a”,"bc"以及“abc”,不过“a”和“bc”并无引用,下次gc也会被回收,常量池中“abc”已经有了,不会创建,“a”,"bc"会被创建,但是也没有引用,不久后也会被回收。
第四个,创建了一个对象的引用s4,堆中创建了一个对象,常量池中已经有了“abc”,不在创建,然后使用intern()方法获取常量池中的字符串。
第五个,创建了一个对象的引用s5,堆中创建了一个对象,不过并未被引用,直接指向了常量池中的字符串。
很显然,s1与s5都是指向常量池中的“abc”,同时intern(),指向的就是“abc”这个字符串第一次创建的对象,是同一个数据,使用==结果也是true,
s2,s3,s4,都是一个对象,使用==结果一定是false。
可能有些小伙伴有点小迷糊,s4也是指向常量池中的“abc”啊,怎么和s1不能==呢?
好吧,大家来看下intern()的源码:
public native String intern()
嗯,只有这一行,是native方法,底层不是java实现的。不过没关系,从这一行中我们就可以找到我们想要的结果了。
我们看下s4的创建过程,首先是创建了一个s4的字符串,然后s4.intern(),那么这个s4.intern()和s4有个毛关系啊?只是一个常量池字符串的获取好不?而且还没有赋值。intern()这个方法明显是有返回值的好不?你这个返回值没有赋值,这句代码s4.intern()几乎就是废的好不,就是明确的说:我就来干扰你,咋地?
好吧,来运行下看看:
public class Test {
public static void main(String[] args) {
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("a")+ new String("bc");
String s4 = new String("abc"); s4.intern();
String s5 = new String("abc").intern();
System.out.print("s1==s5的结果");
System.out.println(s1==s5);
System.out.print("s1==s2的结果");
System.out.println(s1==s2);
System.out.print("s1==s3的结果");
System.out.println(s1==s3);
System.out.print("s1==s4的结果");
System.out.println(s1==s4);
System.out.print("s3==s4的结果");
System.out.println(s3==s4);
}
}
结果和预料的当然一致,另外,不要看我写个描述和结果还分两行,那是用“+”号连接会先连接字符串,然后再对比,那啥子也别玩了,嘿嘿,我还是过了最菜的阶段,已经向高级一点的菜鸟进阶了哈,好,来看下结果:
com.example.demo.Test
s1==s5的结果true
s1==s2的结果false
s1==s3的结果false
s1==s4的结果false
s3==s4的结果false
Process finished with exit code 0
有兴趣的同学可以debug一下,你会发现,这个创建字符串都有一个地址的,具体如何,很容易看出来的哦~
好了今天就到这吧~