有趣的String类

来源:互联网 发布:纪梵希散粉 知乎 编辑:程序博客网 时间:2024/05/18 00:38
这两天被String类搞得有点糊涂,加上在看其他的东西的时候又遇到了一些有关String的解释,所以特意集中研究了一下,发现String类确实比较有趣,说下个人体会.

先看个程序.
不看答案,试试你能看出正确的输出结果吗?
public class Test
{
public static void main(String[] args)
{
  String s1=new String("abc");
  StringBuffer sb1=new StringBuffer("abc");

String s2;
StringBuffer sb2;

s2=s1+"def";
sb2=sb1.append("def");

System.out.println("s1="+s1);
System.out.println("s2="+s2+"/n");
System.out.println("sb1="+sb1);
System.out.println("sb2="+sb2);

}
}

输出结果是:
s1=abc
s2=abcdef

sb1=abcdef
sb2=abcdef

String类被设计成immutable,也就是说是一个不可更改的类.
其意义是,当你对一个String类的实例进行操作的时候,实际上你并没有改变对象本身.
拿 这个例子来说,当执行 s2=s1+"def" 的时候,引用s1所指向的对象的值并没有改变,JVM在内存堆中又开辟了一块空间,存放了一个String对象"abcdef",然后把这个对象的引用赋 给了s2.--------------------变化的是引用的值.
而StringBuffer类则不同,引用sb1所指向的对象的值改变了,然后把同一个引用赋给了sb2--------------引用的值没有变化,变化的是引用所指向的对象的值.
如果你在程序后面再增加下面的语句
if(s1==s2)System.out.println("s1==s2");
else System.out.println("s1!=s2");

if(sb1==sb2)System.out.println("sb1==sb2");
else System.out.println("sb1!=sb2");

你就会发现:s1,s2的地址是不同的;而sb1,sb2的地址是相同的.

========================================================

再看看这段程序,

public class Test
{
public static void main(String[] args)
{
   String s1="abcde";
   String s2="abc";
   String s3=new String("abc");
   String s4="abc";
String s5=s2+"de";
   String s6="abc"+"de";

   if(s2==s3)
   System.out.println("s2==s3");
   else
   System.out.println("s2!=s3");

   if(s2==s4)
   System.out.println("s2==s4");
   else
   System.out.println("s2!=s4");

   if(s5==s6)
   System.out.println("s5==s6");
   else
   System.out.println("s5!=s6");

   if(s1==s5)
   System.out.println("s1==s5");
   else
   System.out.println("s1!=s5");

   if(s1==s6)
   System.out.println("s1==s6");
   else
   System.out.println("s1!=s6");
   }
}

输出结果是:
s2!=s3 //---1
s2==s4 //---2
s5!=s6 //---3
s1!=s5 //---4
s1==s6 //---5

其中,结果1是最容易看出来的,s2和s3的地址肯定是不一样的.但是难点是,你是否知道s2,s3的存储地点都在哪里.换言之,你认为String s3=new String("abc");这个语句创建了几个对象?---这是一个很常见的java试题.
如果你正确理解了这一点,那么结果2也是很显而易见的.
结果3,4,5是最有趣的.也体现了String类的特殊.值得好好想想.
^_^

另外,Integer,Double等基本类型(primative type)的wrap类也是immutable的.
具有和String类相同的特点.

关于内存的分配,我是这样理解的:
内存中主要有这样几种存储空间:
1.栈(Stack),主要用来存储对象的引用(object reference),而对象本身并不存储在栈中。
2.堆(heap),存储对象。每次使用new()关键字,就会在堆中开辟一块空间。
3.静态存储区(Static Storage),用来存储静态数据。就是在整个程序运行期间都存在的数据。
4.常量存储区(Constant Storage),故名思义,当然是用来存储常量了。

另外,基本数据类型(primative type)是特殊的,我认为也存储在栈中。

所 以,我认为,b=b+"def"这个语句,实际上生成了一个对象,不过这个对象不在堆中,而是在常量存储区中(当然,前提是常量存储区中没有 "abcdef"这个对象,否则的话就将已存在的"abcdef"对象的引用赋给b,这也是结果2产生的原因),然后把该对象的引用赋给b.
b的引用(指向的对象)发生了变化,而a的引用仍然不变.


对于结果3,4,5产生的原因我是这样理解的:
直接操纵String对象(String s6="abc"+"de";),和通过其引用来操纵String对象(String s5=s2+"de";)是不同的.这个可能是和String类的内部实现有关.在通过其引用来操纵String对象的时候,我记得是先转换成 StringBuffer对象,然后通过append()方法进行连接,得到最终结果后,再使用toString()方法转换成String对象.最后生 成的这个String对象是常量存储区中具有相同值的String对象的一个拷贝,这个拷贝存储在内存堆(heap)中.
所以造成s1!=s5,这其中实际上是有新的对象生成,而直接操纵String对象,不产生新的对象,直接把常量存储区中的"abcde"对象的引用赋给了s6,所以才有s1==s6的结果.

这只是我的理解,也不知道对不,有错误的地方,还请大牛小牛们指点.
(*_*)