String对象方式的创建方式
public static void main(String[] args) {
//1.new的方式创建
String s = new String("abc");
//2. 字面量字符串赋值
String s1 = "bcd";
//3. 多个字面量字符串拼接
String s2 = "abc" + "de";
//4. 含有变量的拼接
String s3 = s1 + "de";
}
查看main的字节码
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=5, args_size=1
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String abc
6: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V
9: astore_1
10: ldc #5 // String bcd
12: astore_2
13: ldc #6 // String abcde
15: astore_3
16: new #7 // class java/lang/StringBuilder
19: dup
20: invokespecial #8 // Method java/lang/StringBuilder."":()V
23: aload_2
24: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: ldc #10 // String de
29: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: astore 4
37: return
LineNumberTable:
line 12: 0
line 14: 10
line 16: 13
line 18: 16
line 19: 37
LocalVariableTable:
Start Length Slot Name Signature
0 38 0 args [Ljava/lang/String;
10 28 1 s Ljava/lang/String;
13 25 2 s1 Ljava/lang/String;
16 22 3 s2 Ljava/lang/String;
37 1 4 s3 Ljava/lang/String;
//1.new的方式创建
String s = new String("abc");
创建的方式,可以看到
0: new #2 // class java/lang/String
这是在堆上创建了一个对象
//2. 字面量字符串赋值
String s1 = "bcd";
//3. 多个字面量字符串拼接
String s2 = "abc" + "de";
10: ldc #5 // String bcd
12: astore_2
13: ldc #6 // String abcde
15: astore_3
对象s2,s3直接从常量池的取值
ldc 表示从常量池提取数据并压入操作数栈
abcde并没有直接完整的形式出现,但是通过下面的常量池是可以发现abcde是存在常量池的
#6 = String #37 // abcde
所以: 若字符串通过字符串字面量的方式生成,是字符串的对象是从常量池中获取的.
1.必须全部是字符串字面量 2.可以有多个,单个字面量赋值是多个的特例
//4. 含有变量的拼接
String s3 = s1 + "de";
16: new #7 // class java/lang/StringBuilder
19: dup
20: invokespecial #8 // Method java/lang/StringBuilder."":()V
23: aload_2
24: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: ldc #10 // String de
29: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
通过字节码发现只要有(非fianl的)变量的参与拼接,那么jvm会通过新建StringBuilder对象的方式来创建
通过StringBuilder.append()的方法追踪源码,发现是不会共享String的char数组的.
若在s1前面添加final,
//2. 字面量字符串赋值
final String s1 = "bcd";
//4. 含有final变量的拼接
String s3 = s1 + "de";
那么等价于String s3 = "bcd" + "de";
继续变:
String s = new String("abc");
final String s1 = s;
String s3 = s1 + "de";
此时就算s1是final 的,但是因为s不是final 的很有可能变动,所以s1,s3都是通过在堆上新建StringBuilder对象生成字符串对象
对应的常量池
Constant pool:
#1 = Methodref #13.#32 // java/lang/Object."":()V
#2 = Class #33 // java/lang/String
#3 = String #34 // abc
#4 = Methodref #2.#35 // java/lang/String."":(Ljava/lang/String;)V
#5 = String #36 // bcd
#6 = String #37 // abcde
#7 = Class #38 // java/lang/StringBuilder
#8 = Methodref #7.#32 // java/lang/StringBuilder."":()V
#9 = Methodref #7.#39 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#10 = String #40 // de
#11 = Methodref #7.#41 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#12 = Class #42 // com/cqs/learning/java/StringDemo
#13 = Class #43 // java/lang/Object
#14 = Utf8
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 Lcom/cqs/learning/java/StringDemo;
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 args
#24 = Utf8 [Ljava/lang/String;
#25 = Utf8 s
#26 = Utf8 Ljava/lang/String;
#27 = Utf8 s1
#28 = Utf8 s2
#29 = Utf8 s3
#30 = Utf8 SourceFile
#31 = Utf8 StringDemo.java
#32 = NameAndType #14:#15 // "":()V
#33 = Utf8 java/lang/String
#34 = Utf8 abc
#35 = NameAndType #14:#44 // "":(Ljava/lang/String;)V
#36 = Utf8 bcd
#37 = Utf8 abcde
#38 = Utf8 java/lang/StringBuilder
#39 = NameAndType #45:#46 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#40 = Utf8 de
#41 = NameAndType #47:#48 // toString:()Ljava/lang/String;
#42 = Utf8 com/cqs/learning/java/StringDemo
#43 = Utf8 java/lang/Object
#44 = Utf8 (Ljava/lang/String;)V
#45 = Utf8 append
#46 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#47 = Utf8 toString
#48 = Utf8 ()Ljava/lang/String;