最近被问到string的一些基础知识,居然不会,上网看了一些文章总结一下加深理解。<o:p></o:p>
String既可以作为一个对象来使用,又可以作为一个基本类型来使用。导致String类比较特殊,容易混淆。这里指的作为一个基本类型来使用只是指使用方法上的。<o:p></o:p>
比如String s= "Hello",它的使用方法如同基本类型int一样,如int i=1;,<o:p></o:p>
而作为一个对象来使用,则是指通过new关键字来创建一个新对象,如<o:p></o:p>
String s = new String("Hello")。<o:p></o:p>
<o:p> </o:p>
一.作为对象使用<o:p></o:p>
String s1 = new String("Hello");
String s2 = new String("Hello");<o:p></o:p>
String s3 =s2;<o:p></o:p>
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));<o:p></o:p>
System.out.println(s2 == s3);
System.out.println(s2.equals(s3));<o:p></o:p>
以上代码段的打印结果是:<o:p></o:p>
false
true
true
true
结果很好理解<o:p></o:p>
"=="来比较它们所引用的是否是同一个对象时,<o:p></o:p>
equals()方法来比较所封装的字符串内容是否完全相同。<o:p></o:p>
总结:
如String作为一个对象来使用,那么虚拟机将为此创建一个新的String对象,即为此对象分配一块新的内存堆,并且它并不是String缓冲池所拥有的,即它是独立的。<o:p></o:p>
<o:p> </o:p>二.作为基本类型使用<o:p></o:p>
以上是标准的创建对象的动作,但是String对象还可以当作基本类型来使用,<o:p></o:p>
但是它的内部动作其实还是创建了一个对象,分配一块新的、独立的内存堆。<o:p></o:p>
如String s = "Hello";<o:p></o:p>
看到这里,对String对象还没搞清楚的朋友开始疑惑了,你肯定会问,这个使用方法在其内部到底发生了什么?这就是String对象容易混淆的关键!<o:p></o:p>
其实在启动程序时,虚拟机会创建一块String对象的String缓冲池。当String对象作为一个基本类型来使用时,比如:String s = "Hello";;,虚拟机会先在这个String缓冲池内寻找是否有相同值的String对象存在,如果存在,则把这个String对象的引用赋值给s。 如果不存在,虚拟机会先在这个String缓冲池内创建此String对象,然后把引用赋值给s。<o:p></o:p>
说到这里,相信大家已经开始明白了。那么请看下面的代码段:<o:p></o:p>
String s1 = "Hello";
String s2 = "Hello";<o:p></o:p>
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));<o:p></o:p>
以上代码段的打印结果是:<o:p></o:p>
true
true<o:p></o:p>
为什么这个结果?那么来分析一下。首先这两个String对象都是作为一个基本类型来使用的,而不是通过new关键字来创建的,因此虚拟机不会为这两个String对象分配新的内存堆,而是到String缓冲池中来寻找。<o:p></o:p>
首先为s1寻找String缓冲池内是否有与"Hello"相同值的String对象存在,此时String缓冲池内是空的,没有相同值的String对象存在,所以虚拟机会在String缓冲池内创建此String对象,其动作就是new String("Hello");。然后把此String对象的引用赋值给s1。<o:p></o:p>
接着为s2寻找String缓冲池内是否有与"Hello"相同值的String对象存在,此时虚拟机找到了一个与其相同值的String对象,这个String对象其实就是为s1所创建的String对象。既然找到了一个相同值的对象,那么虚拟机就不在为此创建一个新的String对象,而是直接把存在的String对象的引用赋值给s2。<o:p></o:p>
这里既然s1和s2所引用的是同一个String对象,即自己等于自己,所以以上两种比较方法都返回ture。<o:p></o:p>
总结
针对String作为一个基本类型来使用:<o:p></o:p>
1。如果String作为一个基本类型来使用,那么我们视此String对象是String缓冲池所拥有的。
2。如果String作为一个基本类型来使用,并且此时String缓冲池内不存在与其指定值相同的String对象,那么此时虚拟机将为此创建新的String对象,并存放在String缓冲池内。
3。如果String作为一个基本类型来使用,并且此时String缓冲池内存在与其指定值相同的String对象,那么此时虚拟机将不为此创建新的String对象,而直接返回已存在的String对象的引用。<o:p></o:p>
理解了以上内容后,请看以下代码段:<o:p></o:p>
String s1 = "Hello";
String s2 = new String("Hello"); <o:p></o:p>
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));<o:p></o:p>
以上代码段的打印结果是:<o:p></o:p>
false
true<o:p></o:p>
根据上面的小结来进行分析。第一行是把String作为一个基本类型来使用的,因此s1所引用的对象是属于String缓冲池内的。并且此时String缓冲池内并没有与其值相同的String对象存在,因此虚拟机会为此创建一个新的String对象,即new String("Hello");。第二行是把String作为一个对象来使用的,因此s2所引用的对象不属于String缓冲池内的,即它是独立的。通过new关键字,虚拟机会为此创建一个新的String对象,即为它分配了一块新的内存堆。因此"=="比较后的结果是false,因为s1和s2所引用的并不是同一个对象,它们是独立存在的。而equals()方法所返回的是true,因为这两个对象所封装的字符串内容是完全相同的。<o:p></o:p>
现在,相信大家已经完全搞清楚String对象是怎么一回事了:)但是到此并没有结束,因为String对象还有更深层次的应用。<o:p></o:p>
三.intern()方法<o:p></o:p>
这里我将分析一下String对象的intern()方法的应用。
intern()方法将返回一个字符串对象的规范表示法,即一个同该字符串内容相同的字符串,但是来自于唯一字符串的String缓冲池,这样将对重复的消息进行共享机制,从而降低了内存消耗,提高了性能。<o:p></o:p>
String s = new String("Hello");
s = s.intern();<o:p></o:p>
以上代码段的功能实现可以简单的看成如下代码段:<o:p></o:p>
String s = "Hello";<o:p></o:p>
你一定又开始疑惑了?那么你可以先看第二个代码段。第二个代码段的意思就是从String缓冲池内取出一个与其值相同的String对象的引用赋值给s。如果String缓冲池内没有与其相同值的String对象存在,则在其内为此创建一个新的String对象。那么第一段代码的意思又是什么呢?我们知道通过new关键字所创建出的对象,虚拟机会为它分配一块新的内存堆。如果平凡地创建相同内容的对象,虚拟机同样会为此分配许多新的内存堆,虽然它们的内容是完全相同的。拿String对象来说,如果连续创建10个相同内容的String对象(new String("Hello")),那么虚拟机将为此分配10块独立的内存堆。假设所创建的String对象的字符串内容十分大,假设一个Stirng对象封装了<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="1" unitname="m">1M</st1:chmetcnv>大小的字符串内容,那么如果我们创建10个此相同String对象的话,我们将会毫无意义的浪费<st1:chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="9" unitname="m">9M</st1:chmetcnv>的内存空间。我们知道String是final类,它所封装的是字符串常量,因此String对象在创建后其内部(字符串)值不能改变,也因此String对象可以被共享。所以对于刚才提到的假设,我们所创建的10个相同内容的String对象,其实我们只需为此创建一个String对象,然后被其它String变量所共享。要实现这种机制,唯一的、简单的方法就是使用String缓冲池,因为String缓冲池内不会存在相同内容的String对象。而intern()方法就是使用这种机制的途径。在一个已实例化的String对象上调用intern()方法后,虚拟机会在String缓冲池内寻找与此Stirng对象所封装的字符串内容相同值的String对象,然后把引用赋值给引用原来的那个String对象的String类型变量。如果String缓冲池内没有与此String对象所封装的字符串内容相同值的String对象存在,那么虚拟机会为此创建一个新的String对象,并把其引用赋值给引用原来的那个String对象的String类型变量。这样就达到了共享同一个String对象的目的,而原先那个通过new关键字所创建出的String对象将被抛弃并被垃圾回收器回收掉。这样不但降低了内存的使用消耗,提高了性能,而且在String对象的比较上也同样更方便了,因为相同的String对象将被共享,所以要判断两个String对象是否相同,则只需要使用"=="来比较,而无需再使用equals()方法来比较,这样不但使用起来更方便,而且也提高了性能,因为String对象的equals()方法将会对字符串内容拆解,然后逐个进行比较,如果字符串内容十分大的话,那么这个比较动作则大大降低了性能。