这是一个老生常谈的问题了,也是技术面试中针对初学者经常被问到的问题,这里就根据自己的理解做下规整,希望可以帮助更多的人,如果觉得哪里说得不对,请在下方留言,验证问题后我会及时更正。
在讲解String特征之前,需要介绍一下关于String至关重要的概念:字符串常量池。
在上篇的文章中讲述了java中的数据存储结构,不明白的童鞋可以看下: java中的数据存储结构.
字符串常量池:
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为 了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中, 就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突 进行共享。
下面的测试例子可以验证这个解释:
@Test
public void testString(){
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
}
比较的值是true,这是因为每当我们创建一个字符串对象的时候,如果常量池里不存在这个字符串,就会创建一个存储在常量池里,如果存在了,就直接把变量的地址指向常量池里,下面是图形解析:
继续看下面的例子:
@Test
public void testString() {
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);//false
}
“==”比较的是内存中存储对象的地址,返回的是false,这是为什么呢?这里用内存存储图做下分析:
我们根据图中的分析可以看出,s1的值是字符串常量池中的”abc”地址,而s2存储的是堆栈区的”new String(“abc”)”新建的对象地址,所以二者是不同的.
值得注意的是下面这个例子:
@Test
public void testString() {
String str1 = "abc";
String str2 = "a" + "b" + "c";
System.out.println(str1 == str2);//true
}
这是因为java在编译期间会自己先优化的,会合并成一个对象”abc”的,然后在字符串池中保留,所以二者相等,指向同一个内存地址.不信的话打开编译成功后的*.class文件看看,变成了下面这段代码:
@Test
public void testString() {
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);
}
更细节的优化细节你也可以使用javac 编译对应的*.java文件,然后使用javap命令反编译查看.class文件,里面是一些机器指令,类似于汇编语言。
String s = "lo"
String str7 = "Hel" + s;
String str8 = "He" + "llo";
System.out.println("str7 is computed at runtime.");
System.out.println("str8 is created by using string constant expression.");
System.out.println(str7 == str8);//false
这个返回的是false不同于上面的情况,因为String str7 = “Hel” + s;是在运行时才创建的,而”Hel”+s会在堆空间生成一个新的对象temp,将这个对应的地址返回给str7这个引用,所以与str8存储的是字符串常量池”Hello”的地址不同.
还有一些关于String创建了几个对象的问题,相信如果你理解了上面的内容,这个不再是问题了,http://blog.csdn.net/sszgg2006/article/details/50673486这篇博客列举了String对象的创建个数的案例,感兴趣的童鞋可以看看。