Java中String的比较方式(== 和 equals)
基本概念
在Java中,String既可以作为一个对象来使用,又可以作为一个基本类型来使用。
这里指的作为一个基本类型来使用只是指使用方法上的,比如String s = "hello"
,它的使用方法如同基本类型int一样,比如int
i =
1;,而作为一个对象来使用,则是指通过new关键字来创建一个新对象,比如String s = new String("Hello")
Java中String比较的方法有两种:
1)用"=="来比较。这种比较是比较两个String类型变量的引用是否相同(即是否指相同的内存地址)
2)用Object对象的equals()
方法来比较。String对象继承自Object,并且对equals()方法进行了重写。两个String对象通过equals()方法来进行比较时,也就是对String对象的实际内容进行比较。
实际例子
String 作为对象时的比较
1 | String s1 = new String("Hello"); |
两个String对象都是通过new
创建出来的,而new
关键字为创建的每个对象分配一块新的、独立的内存堆,因此当通过"=="来比较它们的引用是否相同时,将返回false;而通过equals()
方法来比较时,则返回true,因为这两个对象所封装的字符串内容是完全相同的。
String作为基本类型时的比较
1 | String s1 = "Hello"; |
由于这两个String对象都是作为一个基本类型来使用的,而不是通过new关键字来创建的,因此虚拟机不会为这两个String对象分配新的内存堆,而是到String缓冲池中来寻找。
什么是String缓冲池?在Java中,由于String(final)是不可改变的,为了提高效率,不重复创建新的字符创,Java引用了String缓冲池的概念。
首先为s1寻找String缓冲池内是否有与"Hello"相同值的String对象存在,此时String缓冲没有相同值的String对象存在,所以虚拟机会在String缓冲池内创建此String对象,其动作就是new String("Hello");。然后把此String对象的引用赋值给s1。
接着为s2寻找String缓冲池内是否有与"Hello"相同值的String对象存在,此时虚拟机找到了一个与其相同值的String对象,这个String对象其实就是为s1所创建的String对象。既然找到了一个相同值的对象,那么虚拟机就不在为此创建一个新的String对象,而是直接把存在的String对象的引用赋值给s2。
这里既然s1和s2所引用的是同一个String对象,即自己等于自己,所以以上两种比较方法都返回ture。 。
对象与基本类型的比较
1 | String s1 = "Hello"; |
由于new
关键字会申请新的内存空间,创建新的对象,因此不会去查找缓存池,即使缓存池中有"Hello",因此两者的内存地址不是一样的,所以第一个输出为false,而两者的内容是一样的,输出为true。
将上面的代码稍作修改
1 | String s1 = "Hello"; |
上面的代码增加了一行s2 = s2.intern();
其作用是从String缓冲池内取出一个与其值相同的String对象的引用赋值给s(假如有的话)。
这样做的原因是如果频繁地创建相同内容的对象,虚拟机分配许多新的内存堆,虽然它们的内容是完全相同的。由于String是final类,因此String对象在创建后不能改变。所以为了节省内存,可以使用String缓冲池,因为String缓冲池内不会存在相同内容的String对象。而intern()
方法就是使用这种机制的途径。
在一个已实例化的String对象s上调用intern()
方法后,虚拟机会在String缓冲池内寻找与此s对象存储内容相同的String对象,如果能找到,则返回对象在缓冲池中的地址,如果找不到,那么虚拟机在缓冲池中以s的内容新建一个对象并返回这个对象的地址。注意需要将s指向返回的缓冲池对象的地址,这样才能通过垃圾回收器回将原先那个通过new关键字所创建出的String对象回收。
因此可以解释上面的s1==s2
返回结果为什么是true了,因为此时,两者的引用相同,均指向缓冲池的对象。
拼接字符串后的比较
上面提到由于String是final类,因此String对象在创建后不能改变,那么像拼接字符串的操作如String s1=s2+s3;
(s2、s3是已赋值的String)应该会产生一个新的对象,用新的地址空间存储。但是这句话也不完全对,详见下面的例子
1 | String s1 = "a"; |
输出如下: 1
2
3s3==s4? true
s3==s5? false
s3==s8? true
s4由"a"、"b"两个常量拼接而成,本来按照上面的说法应该会生成新的内存空间,但是因为"a"、"b"为两个为常量,不可变,在编译期间编译器会把s5="a"+"b"优化为s5="ab"。
s5由s1和s2拼接而成,由于两个变量的相加所以编译器无法优化, 在运行时,会有新的String地址空间的分配,而不是指向缓冲池中的“ab”。所以结果false。
s6虽让也是由两个变量拼接而成,但是这两个变量已经声明为final不可变的了,所以类似于s4,在编译期间编译器也进行了优化确定了s8的值。
参考: http://blog.csdn.net/wangdong20/article/details/8566217 http://www.itxxz.com/a/tea/2014/0814/208.html http://renxiangzyq.iteye.com/blog/549554