存储原理
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"。