您的问题的任何答案都取决于 JVM 实现和当前使用的 Java 版本。我认为在面试中问这个问题是不合理的。
爪哇 8
在我的机器上,使用 Java 1.8.0_201,您的代码段会产生这个字节码
L0
LINENUMBER 13 L0
LDC "First"
ASTORE 1
L1
LINENUMBER 14 L1
LDC "Second"
ASTORE 2
L2
LINENUMBER 15 L2
LDC "Third"
ASTORE 3
L3
LINENUMBER 16 L3
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 4
这证明正在创建5 个对象String
(3 个文字*、1 个StringBuilder
、1 个由 动态生成String
的实例StringBuilder#toString
)。
爪哇 12
在我的机器上,使用 Java 12.0.2,字节码是
// identical to the bytecode above
L3
LINENUMBER 16 L3
ALOAD 1
ALOAD 2
ALOAD 3
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001\u0001"
]
ASTORE 4
由于不涉及中间体,因此神奇地将“正确答案”更改为4 个对象。StringBuilder
*让我们深入挖掘一下。
在以下情况下,可能会隐式创建新的类实例:
- 加载包含字符串文字(§3.10.5)的类或接口可能会创建一个新的 String 对象来表示文字。(如果表示相同 Unicode 代码点序列的字符串先前已被保留,则不会发生这种情况。)
换句话说,当您启动应用程序时,字符串池中已经存在对象。您几乎不知道它们是什么以及它们来自哪里(除非您扫描所有加载的类以查找它们包含的所有文字)。
毫无疑问,java.lang.String
该类将作为必要的 JVM 类加载,这意味着它的所有文字都将被创建并放入池中。
让我们从 的源代码中随机选择一个片段,从中String
挑选几个文字,在我们程序的最开始放置一个断点,然后检查池中是否包含这些文字。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
...
public String repeat(int count) {
// ...
if (Integer.MAX_VALUE / count < len) {
throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
" times will produce a String exceeding maximum size.");
}
}
...
}
他们确实在那里。
作为一个有趣的发现,这个 IDEA 的过滤有一个副作用:我正在寻找的子字符串也已添加到池中。在我申请后,池大小增加了一个("bytes String"
已添加)this.contains("bytes String")
。
这让我们何去何从?
我们不知道"First"
在调用之前是否被创建和实习String str1 = "First";
,所以我们不能坚定地声明该行创建了一个新实例。