1 为什么String对象是不可变的
打开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只是引用类型指向实际对象的地址。
2 关于字符串拼接和常量池
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中不再实现复制实例,只是在常量池记录首次出现的实例的引用。