代码如下:
public class Try {
public static void main(String[] args)
{
String a="123"+"234";
String b="123";
String c=b +"234";
}
}
问题:
String c的值不是相当于“123”+“234”吗?为什么它的值不在常量池中?
反编译后代码如下:
Last modified 2018-1-18; size 464 bytes
MD5 checksum 8029b576121bf5bfe66616167b85f43c
Compiled from "Try.java"
public class Try
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#19 // java/lang/Object."<init>":()V
#2 = String #20 // 123234
#3 = String #21 // 123
#4 = Class #22 // java/lang/StringBuilder
#5 = Methodref #4.#19 // java/lang/StringBuilder."<init>":()V
#6 = Methodref #4.#23 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#7 = String #24 // 234
#8 = Methodref #4.#25 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#9 = Class #26 // Try
#10 = Class #27 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 SourceFile
#18 = Utf8 Try.java
#19 = NameAndType #11:#12 // "<init>":()V
#20 = Utf8 123234
#21 = Utf8 123
#22 = Utf8 java/lang/StringBuilder
#23 = NameAndType #28:#29 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#24 = Utf8 234
#25 = NameAndType #30:#31 // toString:()Ljava/lang/String;
#26 = Utf8 Try
#27 = Utf8 java/lang/Object
#28 = Utf8 append
#29 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#30 = Utf8 toString
#31 = Utf8 ()Ljava/lang/String;
{
public Try();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: ldc #2 // String 123234
2: astore_1
3: ldc #3 // String 123
5: astore_2
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: aload_2
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: ldc #7 // String 234
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: astore_3
26: return
LineNumberTable:
line 4: 0
line 5: 3
line 6: 6
line 7: 26
}
SourceFile: "Try.java"
首先来看看JVM是如何处理String a=“123”+“234”的:
0: ldc #2 // String 123234
2: astore_1
首先它会将常量池中的123234的引用压进操作数栈,然后将该
引用放到类的局部变量表中的第二个位置(即a),所以a的值在常量池中
接下来看看JVM如何处理String c=b+“234”的:
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: aload_2
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: ldc #7 // String 234
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: astore_3
首先会创建一个StringBuilder的引用并压入操作数栈,接着复制一份栈顶的引用,然后压入栈(此时操作数栈中关于该StringBuilder的实例对象的引用有两份,原因待会讲),接着调用StringBuilder的默认构造函数,然后将局部变量表中的“123”压入操作数栈(这个“123”其实是字符串常量池中的),接着调用StringBuilder的append函数,“234”从常量池中压入操作数栈,接着调用append函数,然后调用toString方法进行类型转换,最后从栈中弹出该StringBuilder的引用,写入局部变量表。
为什么要复制一次引用?
在调用StringBuilder的构造器时,会从操作数栈中弹出该StringBuilder的引用作为参数,在一系列操作后,要将该StringBuilder的引用写入到局部变量表中,此时会用到操作数栈中剩余的引用,共用了两次。
由此可见,c的值是由StringBuilder转换过来的,该StringBuilder有申请内存空间,所以c的值不在常量池中。