字符串的创建与存储机制
在我们的编程中,常常会使用到String,那么理解String的创建与存储机制十分重要。
字符串有两种创建方式,分别是
(1)String s1="abc"; s1指向的是String常量池中的字符串
(2)String s2=new String("abc"); s2指向的是堆上的对象
两种创建方式的比较:
(1)String s1=new String("abc"),需要注意的是,这样的创建方式每次都会生成新的对象,与String s2=new String("abc"),表面上内容一样,但s1与s2地址不一样。即s1.equals(s2)返回true,s1==s2返回false。
(2)String s1="abc"与String s2="abc",他们不仅内容相同,首地址也相同。那为什么呢?原来java中存在着一个字符串常量池,里面保存着很多已经创建出来的String对象,最重要的是他们可以被共享。当需要创建一个字符串时,首先会在常量池中寻找此字符串。若存在(判断依据是equals的返回值),则直接获取它的引用。若不存在,则首先创建这个字符串对象,然后将它放入到字符串常量池中,最后再获取它的引用。所以这里的s1与s2其实引用着相同的字符串对象。
下面看一道常见的笔试题:
new String("abc"),创建了几个对象?
分情况进行讨论:
(1)先进行new字符串
String s1=new String("abc");
String s2="abc";
两个:常量池中没有abc,因此第一句执行完毕时,先在常量池中创建abc对象,再在堆上创建abc对象,最后返回堆上的这个字符串的引用。此时new语句一共创建两个对象。注意,常量池中的abc与堆上的abc不是一个对象!
(2)后进行new字符串
String s2="abc";
String s1=new String("abc");
一个:第一条语句执行,由于在常量池中不存在abc字符串,则先在常量池中创建这个字符串,然后返回这个字符串的引用。第二条语句也就是new语句执行后,发现在常量池中已经存在此字符串,则不会在常量池中创建,不过依然还是要在堆上创建abc这个对象。那么new语句,一共创建了一个对象。
继续探讨String的不可变性
大家都知道,我们一旦创建了某个字符串,则不能改变该字符串的值,不过你可能说,下面这段代码就改变了字符串的内容啊
String s1="abc";
s1="def";
确实,表面上是改变了字符串的内容,其实这是jvm搞的鬼。
s1一开始引用着常量池中新创建的abc对象,当第二条语句执行后,常量池新创建def字符串对象,并将其引用赋给s1。原先的abc并没有被改变,也没有消失,它只是静静地躺在常量池中。
可见,我们改变的只是s1的引用,而没有改变引用的对象,因为String对象是不可修改的嘛。