存储原理

String可以表示字符串,它实际上是使用字符数组存储字符的。

String有以下两种赋值方式:

  • 直接赋值
String str = "rose";

在内存堆的字符串常量池创建一个对象,由变量的栈地址指向堆中的字符串变量中的字符数组对象。

  • 间接赋值
String str = new String("rose");

通过关键字new调用String的构造方法来赋值。下面是String.class中的部分源码:

public String(){
    this.value="".value;
}
public String(String original){
    this.value = original.value;
}

以上两种赋值方法的分析与比较

  •  直接赋值

编译器首先在栈中写入变量地址,其次在堆的常量池中查找被赋给变量的常量是否存在,若存在则直接将该变量地址指向这个常量对象,否则将此常量作为新创建的对象写入堆中。

  • 间接赋值

编译器在栈中写入地址后,直接在堆中创建一个对象并由地址指向这个对象。

String s1 = "rose";//直接赋值
String s2 = new String("rose");//间接赋值

String s3 = "rose";

System.out.println(s1==s2);//比较s1和s2的地址是否相同
System.out.println(s1==s3);//比较s1和s3的地址是否相同

输出结果分别为:false /n  true

证明通过直接赋值的s1和s3指向相同的对象,s1和间接赋值的s2指向不同的对象,尽管这两个对象的值一样。因此我们可以得出这样的结论:直接赋值避免了浪费内存,不会创建不必要的新对象。因此我们更倾向于使用直接赋值。 

 String类编译期与运行期分析

在编译期时,如果某一个值可以被确定,即常量,那么就使用已有的对象,否则会创建新的对象。

String a = "a";
String a1 = a+1;//这里的a在编译期被判断为变量
String a2 = "a1";

System.out.println(a1==a2);

由于a1的赋值语句中的变量a在编译期不能被确定,因此在堆中创建了一个新的对象 。因此输出的结果是false.

如果把a变为常量,那输出结果就会是true。

String c = getString();//方法只有在运行期才会被执行
String c1 = c+1;
String c2 = "c1";

private static String getString(){

    return "c";
}

System.out.println(c1==c2);

 这里的c调用了getString方法来赋值,因此在c1的赋值语句在编译阶段时进行赋值连接的时候,c还没有被赋值,也就是不能确定,因此结果同上一种情况,输出为false.同理,如果把c变为常量(一般是在赋值语句开头加关键字final),输出会不会为true呢?很遗憾, 结果仍然是false;因为方法只会在运行期执行,返回"c"。