打开String类,查看源码,可以看到这几个成员变量
private final char[] value; private final int offset; private final int count;可以看到String类是char数组的封装
count表示String的长度
offset表示偏移量
String的真实内容为偏移量和长度在数组中进行定位和截取
String的成员变量被final修饰,也就是说对象new之后,就无法改变其成员变量的值。所以说String对象是不可变的
这里有个误区就是
String a="abc"; a="123";这里a为啥可变? 其实这里要注意String 对象是指在堆区创建的对象。这里的a只是引用类型指向实际对象的地址。
public class StringTest {
public String append(String id) {
.String a="abc" ;
return s;
}
}
.String a="abc";
String a="a"+"b"+"c";
String a=new String("abc");
String a=b+"_";
替换1编译查看字节码
Constant pool:
#1 = Methodref #4.#19 // java/lang/Object."<init>":()V
#2 = String #20 // abc
#3 = Class #21 // StringTest
#4 = Class #22 // java/lang/Object
public java.lang.String append(java.lang.String);
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=2
0: ldc #2 // String abc
2: astore_2
3: aload_2
4: areturn
可以看到字节码常量池里面已经有字符串abc了
在方法append字节码中首先是idc #2 表示将常量池里面的值推入栈顶 并没有new一个string对象。所有可以理解为a此时为常量池abc的引用
替换2编译查看字节码
Constant pool:
#1 = Methodref #4.#19 // java/lang/Object."<init>":()V
#2 = String #20 // abc
#3 = Class #21 // StringTest
#4 = Class #22 // java/lang/Object
常量池同样出现abc,说明代码编译的时候,已经将静态字符串已经做了拼接放入了常量池
替换3编译查看字节码
Constant pool:
#23 = Utf8 abc
public java.lang.String append(java.lang.String);
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=2
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String abc
6: invokespecial #4 // Method java/lang/String."<init>
":(Ljava/lang/String;)V
9: astore_2
10: aload_2
11: areturn
常量池还是出现了abc ,并且在堆区创建了一个string对象。并且用常量池里面的字符串作初始化
* 替换4编译查看字节码
#26 = Utf8 abc
public java.lang.String append(java.lang.String);
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=2
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder.
"<init>":()V
7: aload_1
8: invokevirtual #4 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: ldc #5 // String abc
13: invokevirtual #4 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #6 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
19: astore_2
20: aload_2
21: areturn
LineNumberTable:
line 23: 0
line 24: 20
字节码显示在用“+”拼接的时候,编译时创建了StringBuilder对象,并用其做字符串拼接,最后返回toString方法。但是在for循环里面做字符串拼接的时候需注意
public String append() {
String s = "";
for (int i = 0; i < 100; i++) {
s += "abc";
}
return s;
}
public java.lang.String append();
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 100
8: if_icmpge 37
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.
ppend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc #6 // String abc
24: invokevirtual #5 // Method java/lang/StringBuilder.
ppend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #7 // Method java/lang/StringBuilder.
oString:()Ljava/lang/String;
30: astore_1
31: iinc 2, 1
34: goto 5
37: aload_1
38: areturn
字节码显示每一次for循环都会创建StringBulid对象,所以此处用StringBulid来拼接,避免性能消耗
3 intern方法
String.intern() 作用 如果字符串已经包含一个等于String对象的字符串,则返回池中的对象,否则把对象包含的字符串添加到常量池,并返回引用, 但在jdk1.7中不再实现复制实例,只是在常量池记录首次出现的实例的引用。
