Java 运行时字符串连接计算

Java 运行时字符串连接计算,java,Java,根据JLS(15.28常量表达式),表达式仅包含: i)Literals of primitive type and literals of type String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5) or ii)Simple names (§6.5.6.1) that refer to constant variables (§4.12.4). or iii)... 是一个常量表达式 现在strings1=“a”+“b”是一个常量表达式

根据JLS(15.28常量表达式),表达式仅包含:

i)Literals of primitive type and literals of type String (§3.10.1, §3.10.2, §3.10.3,
§3.10.4, §3.10.5)
or
ii)Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
or
iii)...
是一个常量表达式

现在
strings1=“a”+“b”
是一个常量表达式,在编译时将计算为
“ab”

所以
s1=“ab”

[1] 我说的对吗?根据上面的陈述,现在字符串池中有三个对象:-“a”、“b”、“ab”?

现在,

上述代码将被转换为
s2=“a”+“b”编译后

所以
s2=“ab”将自动存储在字符串池中

但是,

对于
字符串s3=s+s1,代码将被翻译为:

s3=new StringBuilder(String.valueOf(s)).append(s1).toString();
并将创建一个新的字符串对象

因此,
s2==s3
将显示为false

这是否意味着在运行时使用StringBuilder计算的字符串连接结果不会存储在字符串池中,而是进入堆(池外)?

这是否意味着在运行时计算字符串连接的结果 使用StringBuilder不会存储在字符串池中,而是会 进入堆(池外)

对。这是正确的。它将在堆中创建新对象

根据上面的陈述,字符串池中有三个对象-“a”、“b”、“ab”?,我说的对吗

不,你错了。连接是在编译时执行的,只有“ab”对象将存储在字符串池中

这是否意味着在运行时使用StringBuilder计算的字符串连接结果不会存储在字符串池中,而是进入堆(池外)

是的,你在这一点上是正确的


总之,字符串文本和编译时常量字符串表达式的值将被存储(并存储在字符串池中)。其他字符串连接的结果将不会被保留。。。除非您显式调用
String.intern()
。(而且你不应该这样做……因为插入字符串通常弊大于利。)

无论哪种方式,您都应该避免使用
=
来比较字符串。

来自JLS§15.18.1:

15.18.1。字符串连接运算符+

如果只有一个操作数表达式的类型为String,则为String 对另一个操作数执行转换(§5.1.11)以生成 在运行时使用字符串

字符串连接的结果是对字符串对象的引用 这是两个操作数字符串的串联。人物 左操作数的字符位于右操作数的字符之前 新创建的字符串中的操作数

字符串对象是新创建的(§12.5),除非表达式是 编译时常量表达式(§15.28)。

实现可以选择执行转换和串联 在一个步骤中避免创建然后丢弃中间 字符串对象。以提高重复字符串的性能 连接时,Java编译器可以使用StringBuffer类或 减少中间字符串对象数量的类似技术 通过表达式求值创建的

对于基元类型,实现还可以优化 通过直接从基元转换来创建包装器对象 键入一个字符串

所以

  • 常量池(“ab”)中有一个对象。临时工不被保存
  • 同样,常量池中也只有“ab”
  • 新字符串是一个新的
    string
    对象,除非显式插入,否则不会在池中
  • 查看一些字节码很有意义:

    String sa1 = "a"+ "b";
    
    final String sb1 = "a";
    final String sb2 = "b";
    String sb3 = sb1 + sb2;
    
    String sc1 = "a";
    String sc2 = "b";
    String sc3 = "a" + "b";
    String sc4 = sc1 + sc2;
    
    变成

      Code:
       0:   ldc #2; //String ab
       2:   astore_0
       3:   ldc #2; //String ab
       5:   astore_3
       6:   ldc #3; //String a
       8:   astore  4
       10:  ldc #4; //String b
       12:  astore  5
       14:  ldc #2; //String ab
       16:  astore  6
       18:  new #5; //class java/lang/StringBuilder
       21:  dup
       22:  invokespecial   #6; //Method java/lang/StringBuilder."<init>":()V
       25:  aload   4
       27:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       30:  aload   5
       32:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       35:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       38:  astore  7
       40:  return
    
    代码:
    0:ldc#2//字符串ab
    2:astore_0
    3:最不发达国家2//字符串ab
    5:astore_3
    6:最不发达国家3//串a
    8:astore 4
    10:最不发达国家4//b串
    12:astore 5
    14:最不发达国家2//字符串ab
    16:astore 6
    18:新#5//类java/lang/StringBuilder
    21:dup
    22:特别是#6//方法java/lang/StringBuilder。“”:()V
    25:aload 4
    27:invokevirtual#7//方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    30:aload 5
    32:invokevirtual#7//方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    35:invokevirtual#8//方法java/lang/StringBuilder.toString:()Ljava/lang/String;
    38:astore 7
    40:返回
    

    您可以看到,在前两种情况下,“ab”直接从常量池加载。在第三个块中,我们得到了转换
    sc4=new StringBuilder().append(sc1).append(sc2).toString()
    ,它创建了一个新对象。

    是的,使用StringBuilder在运行时计算的字符串没有存储在字符串池中是正确的。因为:

  • 有一个
    new
    操作符(为堆中的对象分配新内存)
  • 我们可以从代码中看到:

    AbstractStringBuilder(整数容量){ 值=新字符[容量]; }


  • 堆中分配了一个新的字符数组引用。

    请注意,编译阶段的字符串池与程序运行时的字符串池不同。@Thorbjørn Ravn Andersen:这一点很好。你能给我一些链接或信息来源,让我了解你的观点的细节吗?如果专家认为这是个好主意,我建议添加一条关于只调用
    String.intern()
    的注释。
    String sa1 = "a"+ "b";
    
    final String sb1 = "a";
    final String sb2 = "b";
    String sb3 = sb1 + sb2;
    
    String sc1 = "a";
    String sc2 = "b";
    String sc3 = "a" + "b";
    String sc4 = sc1 + sc2;
    
      Code:
       0:   ldc #2; //String ab
       2:   astore_0
       3:   ldc #2; //String ab
       5:   astore_3
       6:   ldc #3; //String a
       8:   astore  4
       10:  ldc #4; //String b
       12:  astore  5
       14:  ldc #2; //String ab
       16:  astore  6
       18:  new #5; //class java/lang/StringBuilder
       21:  dup
       22:  invokespecial   #6; //Method java/lang/StringBuilder."<init>":()V
       25:  aload   4
       27:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       30:  aload   5
       32:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       35:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       38:  astore  7
       40:  return