Ответ 1
Любой ответ на ваш вопрос будет зависеть от реализации JVM и используемой в настоящее время версии Java. Я думаю, что это необоснованный вопрос в интервью.
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 объектов создаются (3 String
литерала *, 1 StringBuilder
, 1 динамически создаваемый экземпляр String
от StringBuilder#toString
).
Java 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
не задействован.
* Давай копай немного глубже.
12,5. Создание экземпляров нового класса
Новый экземпляр класса может быть неявно создан в следующих ситуациях:
- Загрузка класса или интерфейса, который содержит строковый литерал (§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.");
}
}
...
}
Они действительно там.
As an interesting find, this IDEA 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 ([TG410] was added) after I applied [TG411].
Куда это нас приведет?
Мы не знаем, был ли "First"
создан и интернирован, прежде чем мы вызовем String str1 = "First";
, поэтому мы не можем твердо заявить, что линия создает новый экземпляр.