如何在Java中完成最终字符串的连接?

Ank*_*and 17 java string final

当我编译这个片段.

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

    }
}
Run Code Online (Sandbox Code Playgroud)

其中产生以下字节代码

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
Run Code Online (Sandbox Code Playgroud)

所以字符串文字"字符串"已存在于常量池中,该字符串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"将在行共享相同的内存String str= str1+str2; 但是str1 + str2如何直接在常量字符串池中生成"string".不调用任何String Builder类就像我不写时一样final.?看看它是否与实习生有关

我写了这个片段

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

    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

实际上它也stringBuilder用于连接.所以它已经做了最后的事情.关于final字符串有什么特别之处,我绝对不知道吗?

JB *_*zet 17

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

引用常量变量(第4.12.4节)的简单名称(第6.5.6.1节)是常量表达式.

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;
Run Code Online (Sandbox Code Playgroud)

编译方式与.相同

String str = "str" + "ing";
Run Code Online (Sandbox Code Playgroud)

这与编译方式相同

String str = "string";
Run Code Online (Sandbox Code Playgroud)

  • 正如我所说,连接是由编译器本身完成的,而不是在运行时完成的.为`String str = str1 + str2`行生成的字节代码与为`String str ="string"`行生成的字节代码完全相同,因为`str1 + str2`是一个常量表达式. (6认同)
  • "因为str1 + str2是一个常量表达式"或更准确,因为`str1`和`str2`不仅仅是任何常量,而是*编译时常量*允许编译器计算一次并将结果放在代码中而不是重新计算它在运行时每次执行此代码. (3认同)