以下为JDK1.6的说法
字符串内存结构图:
注意:字符串在底层的存储实际就是使用字符数组进行存储。
String str1 = "JavaEE";
首先在栈空间声明一个str1变量,然后看字符串常量池是否存在”JavaEE”,如果不存在,则在字符串常量池中新建”JavaEE”(字符数组)。这个值存在一个首地址,将这个首地址赋给str1。
String str2 = "JavaEE";
字符串常量区已经存在”JavaEE“,所以直接将”JavaEE“的首地址赋给str2。
String str3 = new String("JavaEE");
面试题:上述过程中创建了几个对象?答案是两个。
首先既然是new的,那么就要在堆空间开辟一个区域。但是记住字符串的真正内容并没有放在堆空间中,仍然在字符串常量池中。
可以从上面的代码看出:将字符串常量区的”JavaEE“的首地址(value–字符数组名)赋给刚才堆空间new出的一个实体中的value属性,然后再将堆空间中的实体的地址赋给str3.
String str5 = "Android";
str5 = str5 + "handoop";
首先也是在栈空间声明一个str5变量,然后看字符串常量池是否存在”Android”,如果不存在,则在字符串常量池中新建”Android”(字符数组)。这个值存在一个首地址,将这个首地址赋给str5。
第二步str5 = str5 + “handoop”;。这个要注意,并不是在字符串常量池中的”Android“后面直接加上”handoop“,而是重新在字符串常量池中新建一个”Androidhandoop“。
至于为什么是重新新建了,而不是在后面添加,这个很好理解。在前面已经提到字符串在底层的存储是以字符数组的形式的,数组在创建的时候大小已经确定了,如果直接在后面添加是不一定能放得下的。
简单示例:
import org.junit.Test;
public class TestString {
@Test
public void test(){
String str1 = "JavaEE";
String str2 = "JavaEE";
String str3 = new String("JavaEE");
String str4 = "JavaEE" + "Android";
String str5 = "Android";
String str6 = str1 + str5;
str5 = str5 + "handoop";
String str7 = str6.intern();
String str8 = "JavaEEAndroid";
System.out.println(str1 == str2);//true
System.out.println(str1 == str3);//false
System.out.println(str1.equals(str3));//true
System.out.println(str4 == str6);//false
System.out.println(str4.equals(str6));//true
System.out.println(str7 == str4);//true
System.out.println(str4 == str8);//true
Person p1 = new Person("AA");
Person p2 = new Person("AA");
System.out.println(p1.name == p2.name);//true
}
}
class Person{
String name;
Person(String name){
this.name = name;
}
}
有几个地方解释一下:
(1)
System.out.println(str4 == str6);//false 为什么?
str4是在编译期就确定的值,为”JavaEEAndroid”。
str6涉及到变量的操作,编译期不确定具体值,加号两边在运行时会转换成new StringBuilder(str1).append(str5).toString(),而这个会产生一个新的字符串对象,所以与str4不相等。
(2)
String str4 = "JavaEE" + "Android";
String str1 = "JavaEE";
String str5 = "Android";
String str6 = str1 + str5;
String str7 = str6.intern(); //intern()的作用是什么?
System.out.println(str7 == str4);//true
intern()方法设计的初衷,就是重用String对象,以节省内存消耗。
详情见此 Java技术——你真的了解String类的intern()方法吗
Person p1 = new Person("AA");
Person p2 = new Person("AA");
System.out.println(p1.name == p2.name);//true
从下面的内存结构图中可以看出显然是true。