String内存位置
基本类型的变量数据和对象的引用都是放在栈里面,String常量放在常量池里面,String对象是放在堆里面。
常量池是放在方法去里面的,从jdk7开始就移到了堆里面。
String常量:
String s1="hello";
String s2="hello";
System.out.println(s1==s2);
- 第一句代码执行后就在常量池中创建了一个值为hello的String对象;
- 第二句执行时,因为常量池中存在hello所以就不再创建新的String对象了。
- 此时该字符串的引用在虚拟机栈里面。
- 因为s1和s2指向的是同一个对象,所以s1==s2
String对象
String a = new String("skj");
String b = new String("skj");
System.out.println(a==b);
- Class被加载时就在常量池中创建了一个值为skj的String对象,第一句执行时会在堆里创建new String("skj")对象;
- 第二句执行时,因为常量池中存在skj所以就不再创建新的String对象了,直接在堆里创建new String("skj")对象。
- a,b指向的是两个不同的对象,所以a!=b
String本质
Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings.Because String objects are immutable they can be shared.
String是值不可变(immutable)的常量,是线程安全的(can be shared),不可变对象不能被写,所以线程安全。
接下来,String类使用了final修饰符,表明了String类的第二个特点:String类是不可继承的。
下面是String类的成员变量定义,从类的实现上阐明了String值是不可变的(immutable)。
private final char value[];
这是一个final 字符数组,String的本质还是一个不可变的字符数组。
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
这是String的concat方法。
实现该方法第一步要做的肯定是扩大成员变量value的容量,扩容的方法重新定义一个大容量的字符数组buf。
第二步就是把原来value中的字符copy到buf中来,再把需要concat的字符串值也copy到buf中来,这样子,buf中就包含了concat之后的字符串值。
下面就是问题的关键了,如果value不是final的,直接让value指向buf,然后返回this,则大功告成,没有必要返回一个新的String对象。但是。。。可惜。。。由于value是final型的,所以无法指向新定义的大容量数组buf,那怎么办呢?“return new String(0, count + otherLen, buf);”,这是String类concat实现方法的最后一条语句,重新new一个String对象返回。
由此可见,String的本质是一个字符数组,而且不可变。