On m'a demandé dans une interview sur le nombre d'objets qui seront créés sur le problème donné:
String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;
J'ai répondu qu'il y aurait 6 objets créé dans le pool de chaînes.
3 serait pour chacune des trois variables.
1 serait pourstr1 + str2
(disonsstr
).
1 serait pourstr2 + str3
.
1 serait pour lestr + str3
(str = str1 + str2
).
La réponse que j'ai donnée est-elle correcte? Sinon, quelle est la bonne réponse?
Toute réponse à votre question dépendra de l'implémentation de la JVM et de la version Java actuellement utilisée. Je pense que c'est une question déraisonnable à poser dans une interview.
Sur ma machine, avec Java 1.8.0_201, votre extrait de code entraîne ce 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
ce qui prouve que 5 objets sont en cours de création (3 String
littéraux *, 1 StringBuilder
=, 1 instance String
produite dynamiquement par StringBuilder#toString
).
Sur ma machine, avec Java 12.0.2, le bytecode est
// 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
qui comme par magie change "la bonne réponse" en 4 objets car il n'y a pas d'intermédiaire StringBuilder
impliqué.
* Creusons un peu plus profondément.
12.5. Création de nouvelles instances de classe
Une nouvelle instance de classe peut être implicitement créée dans les situations suivantes:
- Le chargement d'une classe ou d'une interface qui contient un littéral de chaîne ( §3.10.5 ) peut créer un nouvel objet String pour représenter le littéral. (Cela ne se produira pas si une chaîne indiquant la même séquence de points de code Unicode a déjà été internée.)
En d'autres termes, lorsque vous démarrez une application, il existe déjà des objets dans le pool de chaînes. Vous savez à peine ce qu'elles sont et d'où elles viennent (sauf si vous scannez toutes les classes chargées pour tous les littéraux qu'elles contiennent).
La classe Java.lang.String
Sera sans aucun doute chargée en tant que classe JVM essentielle, ce qui signifie que tous ses littéraux seront créés et placés dans le pool.
Prenons un extrait choisi au hasard dans le code source de String
, en choisissons quelques littéraux, mettons un point d'arrêt au tout début de notre programme et examinons si le pool contient ces littéraux.
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.");
}
}
...
}
Ils sont bien là.
Une découverte intéressante, le filtrage de cette IDEA a un effet secondaire: les sous-chaînes que je cherchais ont également été ajoutées à la piscine. La taille du pool a augmenté d'une unité ("bytes String"
A été ajouté) après avoir appliqué this.contains("bytes String")
.
Où cela nous mène-t-il?
Nous n'avons aucune idée si "First"
A été créé et interné avant d'appeler String str1 = "First";
, Nous ne pouvons donc pas affirmer avec fermeté que la ligne crée une nouvelle instance.
Avec les informations fournies, la question ne peut pas être définitivement résolue. Comme indiqué dans le JLS, §15.18.1 :
... Pour augmenter les performances de la concaténation répétée de chaînes, un compilateur Java peut utiliser la classe
StringBuffer
ou une technique similaire pour réduire le nombre d'objets String intermédiaires créés par évaluation d'une expression.
Cela signifie que la réponse dépend au moins du concret Java utilisé.
Je pense que le mieux que nous puissions faire est de donner un intervalle comme réponse:
str1
à str3
ne sont jamais utilisés et replient la concaténation pendant la compilation, de sorte qu'un seul objet String
- est créé (celui référencé par str4
)String
s créé doit être de 5: un pour str1
à str3
, un pour tmp = str1 + str2
et un pour str4 = tmp + str3
.Donc ... ma réponse serait "quelque chose entre un à cinq String
- objets". Quant au nombre total d'objets créés juste pour cette opération ... je ne sais pas. Cela peut également dépendre de la manière exacte, par exemple StringBuffer
est implémenté.
En passant: je me demande quelle est la raison de poser de telles questions. Normalement, on n'a pas besoin de se soucier de ces détails.
Java 8 créera probablement 5 objets:
StringBuilder
String
concaténéAvec Java 9 les choses ont changé cependant et la concaténation String
n'utilise plus StringBuilder
.
Il devrait être de 5:
trois pour les trois littéraux (affectés à str1
, str2
et str3
)
un pour str1 + str2
un pour (result from the previous operation) + str3
(assigné à str4
)
Une implémentation conforme Java peut concaténer les chaînes de plusieurs façons, au moment de l'exécution ou au moment de la compilation, nécessitant un certain nombre d'objets au moment de l'exécution, y compris zéro objet s'il détecte que le résultat n'est pas nécessaire au moment de l'exécution.
4 objets de chaîne seront créés dans le pool de constantes de chaîne. 3 pour les littéraux et 1 avec concaténation.
si nous utilisons
String s1 = new String("one")
il créera deux objets, un en pool constant et un en mémoire de tas.
si on définit:
String s1 = "one";
String s2 = new String("one");
il créera deux objets, un en pool constant et un en mémoire de tas.
L'opération de concaténation ne crée pas ces nombreux objets String. Il crée un StringBuilder
puis ajoute les chaînes. Il peut donc y avoir 5 objets, 3 (variables) + 1 (sb) + 1 (chaîne concaténée).