String 基本常识
- 通过直观看代码得到的 String 是 final 的不可继承
- String 对象是不可变的。查看源码或者JDK文档可以看出来,String里面每一个会修改String值的方法,都会创建一个新的对象
- 基于 特征2 所以涉及到String的效率问题
String 内存分配
- 常量池
- 堆
这里面有个隐藏的问题,就是关于常量池的位置。
在JDK1.6以前(包括JDK1.6)常量池是在*方法区的
在JDK1.7的时候常量池移到了堆
具体的变化请自行参考JVM相关资料
String a = "a";
String b = "a";
String c = new String("b");
String d = new String("a");
String e = new String("b");
直接赋值
首先在常量池判断是否存在a,如果不存在则在常量池开辟一块空间存放a,如果存在则不用开辟空间。
然后在栈中开辟一块空间存放常量池中a的内存地址,并且命名为a。
通过new
首先在常量池判断是否存在b,如果不存在则在常量池开辟一块空间存放b,如果存在则不用开辟空间。
然后在内存堆中开辟一块空间存放new出来的对象
然后在栈中开辟一块空间存放内存堆中a的内存地址,并且命名为c。
关于堆里面的String对象和常量池里面的String常量之间的的联系,一脸懵逼,望高手解答
String 的比较主要是 == equals
通过这俩个判断可以验证上面的问题
System.out.println(a == b);
System.out.println(a == d);
System.out.println(c == e);
System.out.println(a.equals(b));
System.out.println(a.equals(d));
System.out.println(c.equals(e));
输出
true
false
false
true
true
true
关于 == 和 equals 请参考Java equals == 简单分析
a与b都是存放常量池的内存地址,所以a与b == 返回 true
a与b内容一致,所以a与b equals 返回true
d存放的是堆的内存地址,所以a与d == 返回 false
a与d内容一致,所以a与b equals 返回true
c与e存放的是不同的堆的内存地址,所以c与e == 返回 false
c与e内容一致,所以a与b equals 返回true
String StringBuffer StringBuilder
基于 String 的不可变行带来的效率问题引出来
StringBuffer StringBuilder
后俩者的主要是区别,可以直接通过代码得到
StringBuffer
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuilder
public StringBuilder append(String str) {
super.append(str);
return this;
}
分析
String通过“+”进行操作的时候,由于String是不可变的,所以每次操作都会产生一个新的String对象,依次类推,最后会产生一大堆需要垃圾回收的中间对象。
这个要说明一个问题,就是本身编译器是使用StringBuilder对代码进行了优化了。但是还是不是最优的,可以通过 javap 进行查看
public String stringAppend() {
String result = "";
for (int i = 0; i < 10; i++) {
result += String.valueOf(i);
}
return result;
}
public String sbAppend() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
sb.append(i);
}
return sb.toString();
}
public java.lang.String stringAppend();
Code:
0: ldc #2 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 39 // 开始一个循环体
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V // 这个地方很关键 在循环体里面去构造对象
18: aload_1
19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokestatic #6 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
26: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_1
33: iinc 2, 1
36: goto 5 // 结束循环体
39: aload_1
40: areturn
public java.lang.String sbAppend();
Code:
0: new #3 // class java/lang/StringBuilder
3: dup
4: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V // 只构造一次对象
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: bipush 10
13: if_icmpge 28
16: aload_1
17: iload_2
18: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
21: pop
22: iinc 2, 1
25: goto 10
28: aload_1
29: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: areturn
我只能说显而易见了