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的本质是一个字符数组,而且不可变。