本文出自 “讨厌萝卜的试验田” 博客出处http://cymoft.blog.51cto.com/324099/473220
1.String s = new String("abc");创建了几个String对象。
如果String常理池中,已经创建"xyz",则不会继续创建,此时只在堆内存中创建了一个对象new String("xyz");
.如果String常理池中,没有创建"xyz",则会创建两个对象,一个对象的值是"xyz",一个对象堆内存中new String("xyz")。
2.String s1 = "abc";
String s2 = "a";
String s3 = s2 + "bc";
String s4 = "a" + "bc";
String s5 = s3.intern();
请问s1==s3是true还是false,s1==s4是false还是true。s1==s5呢?
这样的题碰到很多了,今天来全面的总结一下,之所以敢说全面,是因为我参考的是最权威的资料……Java语言规范和Java API。
API中说明:
字符串是常量;它们的值在创建之后不能更改。因为 String 对象是不可变的,所以可以共享。(http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/java/lang/String.html)
那么,是如何共享的勒,String类的intern方法有如下说明:
字符串池,初始为空,它由类 String 私有地维护。(http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/java/lang/String.html#intern())
也就是说,String类自己维护了一个池,把程序中的String对象放到那个池里面,于是我们代码中只要用到值相同的String,就是同一个String对象,节省了空间。
但是,并不是任何时候任何String对象都在这个字符串池中,不然也就不会有那些面试题了。那么,到底哪些String对象在池中,哪些在堆中?请看Java语言规范。
Java语言规范第三版,第3.10.5小节,String Literals的最后一段如下:
首先解释下什么是字符串字面常数(String Literals),字面常数(Literals)就是你写在源代码里面的值,比如说int i = 6; 6就是一个整数形字面常数。String s = "abc"; “abc”就是一个字符串字面常数。Java中,所有的字符串字面常数都放在上文提到的字符串池里面,是可以共享的,就是说,String s1 = "abc"; String s2 = "abc"; s1,s2都引用的同一个字符串对象,而且这个对象在字符串池里面,因此s1==s2。另外,字符串字面常数是什么时候实例化并放到字符串池里面去的呢?答案是Load Class的时候(Java Spec 12.5)。
任何类任何包,值相同的字符串字面常数(String Literals)都引用同一个对象。
第四句是说,通过常量表达式(constant expressions)计算出来的字符串,也算字符串字面常数,就是说他们也在字符串池中。什么是常量表达式(constant expressions)待会说,这个很重要。
注意第五句话,在程序运行时通过连接(+)计算出来的字符串对象,是新创建的,他们不是字面常数,就算他们值相同,他们也不在字符串池里面,他们在堆内存空间里,因此引用的对象各不相同。
最后一句话也很重要,String类的intern方法,返回一个值相同的String对象,但是这个对象就像一个字符串字面常数一样,意思就是,他也到字符串池里面去了。
现在我们来看开头的两个题目。
1.String s = new String("abc");创建了几个String对象。
答案是2个,一个是字符串字面常数,在字符串池中。一个是new出来的字符串对象,在堆中。
2.String s1 = "abc";
String s2 = "a";
String s3 = s2 + "bc";
String s4 = "a" + "bc";
String s5 = s3.intern();
请问s1==s3是true还是false,s1==s4是false还是true。s1==s5呢?
此题注意两点,因为s2是一个变量,所以s3是运行时才能计算出来的字符串,是new的,在堆中不在字符串池中。s4是通过常量表达式计算出来的,他等同于字符串字面常数,在字符串池中。所以,s1!=s3,s1==s4。再看s5,s5是s3放到字符串池里面返回的对像,所以s1==s5。这里新手要注意的是,s3.intern()方法,是返回字符串在池中的引用,并不会改变s3这个变量的引用,就是s3还是指向堆中的那个"abc",并没有因调用了intern()方法而改变,实际上也不可能改变。