根据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)...
是一个常量表达式。
现在String s1="a"+"b";
是一个常量表达式,在编译时将被评估为"ab"
。
所以s1="ab";
[1] 我正确地说,现在根据上述声明,字符串池中有三个对象: - “a”,“b”,“ab”???
现在,
final String s="a";
final String s1="b";
String s2=s+s1; // is also constant expression and get evaluated at compile time.
编译后,上述代码将被转换为s2="a"+"b";
。
所以s2="ab";
将自动存储在字符串池中。
但是,
// note there is no final now.
String s="a";
String s1="b";
String s2="a"+"b"; // constant expression.
String s3=s+s1; // is NOT a constant expression and get evaluated at RUN TIME.
对于String s3=s+s1;
,代码将转换为:
s3=new StringBuilder(String.valueOf(s)).append(s1).toString();
并将创建一个新的String对象。
因此,s2==s3
将会出现错误;
这是否意味着使用StringBuilder在运行时计算的字符串连接结果不会存储在字符串池中,而是进入堆(池外)?
答案 0 :(得分:3)
来自JLS§15.18.1:
15.18.1。字符串连接运算符+
如果只有一个操作数表达式是String类型,则为string 转换(第5.1.11节)在另一个操作数上执行以产生a 运行时的字符串。
字符串连接的结果是对String对象的引用 这是两个操作数字符串的串联。那些角色 左手操作数的前面是右手的字符 新创建的字符串中的操作数。
除非表达式是a,否则新创建String对象(第12.5节) 编译时常量表达式(§15.28)。
实现可以选择执行转换和连接 在一步中避免创建然后丢弃中间体 字符串对象。增加重复字符串的性能 连接,Java编译器可以使用StringBuffer类或a 类似的技术减少了中间String对象的数量 通过评估表达式创建的。
对于原始类型,实现也可以优化掉 通过直接从基元转换来创建包装器对象 键入字符串。
所以,
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
您可以看到,在前两种情况下,“ab”直接从常量池中加载。在第三个块中,我们得到翻译sc4 = new StringBuilder().append(sc1).append(sc2).toString()
,它创建了一个新对象。
答案 1 :(得分:1)
我正确地说,现在根据上述声明,字符串池中有三个对象: - “a”,“b”,“ab”???
没有。你错了。连接在编译时执行,只有“ab”对象将存储在字符串池中。
这是否意味着使用StringBuilder在运行时计算的字符串连接的结果不会存储在字符串池中,而是进入堆(池外)?
是的,你在这一点上是正确的。
总之,字符串文字和编译时常量字符串表达式的值将被实现(并存储在字符串池中)。除非您明确调用String.intern()
,否则其他字符串连接的结果将不会被实现。 (你应该很少这样做......因为实习字符串通常弊大于利。)
无论哪种方式,都应该避免使用==
来比较字符串。
答案 2 :(得分:0)
这是否意味着在运行时评估字符串连接的结果 使用StringBuilder不会存储在String Pool中,而是存在 进入堆(外部池)
是。那是正确的。它将在堆中创建新对象。
答案 3 :(得分:0)
是的,你是正确的,使用StringBuilder在运行时评估的字符串不会存储在字符串池中。这是因为:
new
运算符(为堆中的对象分配新内存)正如我们可以看到的那样:
AbstractStringBuilder(int capacity){ value = new char [capacity]; }
在堆中分配了一个新的char数组引用。