Any answer to your question will depend on the JVM implementation and the Java version currently being used. I think it's an unreasonable question to ask in an interview.
Java 8
On my machine, with Java 1.8.0_201, your snippet results in this bytecode
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
which proves that 5 objects are being created (3 String
literals*, 1 StringBuilder
, 1 dynamically produced String
instance by StringBuilder#toString
).
Java 12
On my machine, with Java 12.0.2, the bytecode is
// 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:
"u0001u0001u0001"
]
ASTORE 4
which magically changes "the correct answer" to 4 objects since there is no intermediate StringBuilder
involved.
*Let's dig a bit deeper.
A new class instance may be implicitly created in the following situations:
- Loading of a class or interface that contains a string literal (§3.10.5) may create a new String object to represent the literal. (This will not occur if a string denoting the same sequence of Unicode code points has previously been interned.)
In other words, when you start an application, there are already objects in the String pool. You barely know what they are and where they come from (unless you scan all loaded classes for all literals they contain).
The java.lang.String
class will be undoubtedly loaded as an essential JVM class, meaning all its literals will be created and placed into the pool.
Let's take a randomly selected snippet from the source code of String
, pick a couple of literals from it, put a breakpoint at the very beginning of our programme, and examine if the pool contains these literals.
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.");
}
}
...
}
They are there indeed.
As an interesting find, this IDEA's filtering has a side effect: the substrings I was looking for have been added to the pool as well. The pool size increased by one ("bytes String"
was added) after I applied this.contains("bytes String")
.
Where does this leave us?
We have no idea whether "First"
was created and interned before we call String str1 = "First";
, so we can't state firmly that the line creates a new instance.