最终字符串的串联是如何在Java中完成的?

2022-09-02 21:49:14

当我编译这个片段时。

public class InternTest {
    public static void main(String...strings ){
        final String str1="str";
        final String str2="ing";
        String str= str1+str2;

    }
}

生成以下字节码

public static void main(java.lang.String...);
   flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
   Code:
     stack=1, locals=4, args_size=1
        0: ldc           #16                 // String str
        2: astore_1
        3: ldc           #18                 // String ing
        5: astore_2
        6: ldc           #20                 // String string
        8: astore_3
        9: return

所以字符串文字“字符串”已经存在于常量池中,该池在此行被推到堆栈上。6: ldc #20 // String string

引用 JSL

来自 JLS §4.12.4 - 最终变量:

基元类型或 String 类型的变量称为常量变量,它是 final 并使用编译时常量表达式 (§15.28) 初始化。

同样来自 JLS §15.28 - ConstantExpression:

String 类型的编译时常量表达式始终是“暂存的”,以便使用方法 String#intern() 共享唯一的实例。

所以我知道 str1 和 str2 将在创建的那一刻就被拘留。str“和”ing“将在第一行共享相同的内存,但是str1 + str2如何在常量字符串池中直接生成”字符串”。无需调用任何字符串生成器类,就像我不写 final 时所做的那样。?看看它是否与实习生的事情有关String str= str1+str2;

我写了这个片段

public class IntermTest {
    public static void main(String...strings ){
         String str1=("str").intern();
        String str2=("ing").intern();
        String str= str1+str2;

    }
}

但是当我生成字节码时,我得到了这个

public static void main(java.lang.String...);
    flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
    Code:
      stack=3, locals=4, args_size=1
         0: ldc           #16                 // String str
         2: invokevirtual #18                 // Method java/lang/String.intern:
()Ljava/lang/String;
         5: astore_1
         6: ldc           #24                 // String ing
         8: invokevirtual #18                 // Method java/lang/String.intern:
()Ljava/lang/String;
        11: astore_2
        12: new           #26                 // class java/lang/StringBuilder
        15: dup
        16: aload_1
        17: invokestatic  #28                 // Method java/lang/String.valueOf
:(Ljava/lang/Object;)Ljava/lang/String;
        20: invokespecial #32                 // Method java/lang/StringBuilder.
"<init>":(Ljava/lang/String;)V
        23: aload_2
        24: invokevirtual #35                 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        27: invokevirtual #39                 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
        30: astore_3
        31: return

实际上,它也用于串联。所以它对最终做了一些事情。字符串有什么特别之处,我绝对不知道吗?stringBuilderfinal


答案 1

http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28

引用常量变量的简单名称 (§6.5.6.1) (§4.12.4) 是常量表达式。

http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28 还说:

常量表达式是表示基元类型的值或 String 的表达式,它不会突然完成,并且仅使用以下内容组成:

  • 基元类型的文本和字符串类型的文本 (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)
  • [...]
  • 加法运算符 + 和 - (§15.18)
  • [...]
  • 引用常量变量的简单名称 (§6.5.6.1) (§4.12.4)。

例 15.28-1.常量表达式

[...]

“整数 ” + Long.MAX_VALUE + “ 非常大。

由于这两个变量是常量表达式,因此编译器会进行串联:

String str = str1 + str2;

编译方式与

String str = "str" + "ing";

其编译方式与

String str = "string";

答案 2