Utiliser des littéraux de chaîne identiques au lieu d'une variable finale?
J'ai rencontré une classe qui inclut plusieurs utilisations d'un littéral de chaîne, "foo".
Ce que j'aimerais savoir, c'est quels sont les avantages et l'impact (en termes de création d'objet, d'utilisation de la mémoire et de vitesse) de l'utilisation de cette approche au lieu de déclarer la chaîne comme finale et de remplacer tous les littéraux par la variable finale?
Par exemple (bien que ce ne soit évidemment pas un vrai usage de mot):
private static final String FINAL_STRING = "foo";
public void stringPrinter(){
for(int i=0;i<10;i++){
System.out.println(FINAL_STRING);
}
}
Versus:
public void stringPrinter(){
for(int i=0;i<10;i++){
System.out.println("foo");
}
}
Qui est préférable et pourquoi (en supposant la chaîne valeur restera constante)?
Le (deuxième) exemple ci-dessus entraînerait-il la création de 10 objets de chaîne ou la JVM réaliserait-elle qu'un seul littéral est réellement utilisé et créerait une seule référence. Si oui, y a-t-il un avantage à déclarer la chaîne comme finale (comme dans le premier exemple)?
Si le code interprété remplace le littéral de chaîne par une seule référence, cela s'applique-t-il toujours si le même littéral se produit à plus d'un endroit:
public void stringPrinter(){
for(int i=0;i<5;i++){
System.out.println("foo"); // first occurence
System.out.println("foo"); // second occurence
}
}
6 réponses
Ils seront exactement les mêmes. Le littéral est interné (toute expression constante de temps de compilation qui résulte en cette chaîne partage la même instance que toutes les autres constantes/littéraux) dans les deux cas et un compilateur intelligent+runtime ne devrait avoir aucun problème à réduire les deux à l'exemple le plus optimisé.
L'avantage vient plus dans la maintenabilité. Si vous voulez changer le littéral, vous n'aurez besoin que de changer une occurrence avec une constante, mais vous devrez rechercher et modifier chaque exemple s'ils ont été inclus en ligne.
Du JLS
les constantes de compilation de type String sont toujours "internées" de manière à partager des instances uniques, en utilisant la méthode String.stagiaire.
Donc, non, il n'y aura qu'un seul objet string.
Comme le note Mark, c'est strictement la question de la maintenabilité et non de la performance.
L'avantage n'est pas dans la performance, mais dans la maintenabilité et la fiabilité.
Permettez-moi de prendre un exemple réel que je suis tombé tout récemment. Un programmeur a créé une fonction qui a pris un paramètre de chaîne qui a identifié le type d'une transaction. Ensuite, dans le programme, il a fait une comparaison de chaîne par rapport à ce type. Comme:
if (type.equals("stock"))
{ ... do whatever ... }
Puis il a appelé cette fonction, en lui passant la valeur "Stock".
Remarquez-vous la différence de capitalisation? Pas plus que l'original programmeur. Il s'est avéré être un bug assez subtil à comprendre, parce que même en regardant les deux listes, la différence de capitalisation ne m'a pas frappé.
Si au lieu de cela il avait déclaré une statique finale, disons
final static String stock="stock";
Ensuite, la première fois qu'il a essayé de passer en "Stock" au lieu de "stock", il aurait eu une erreur de compilation.
Mieux encore dans cet exemple aurait été de faire une énumération, mais supposons qu'il ait dû écrire la chaîne dans un fichier de sortie ou quelque chose de ce genre être une chaîne de caractères.
L'utilisation de la statique finale donne au moins x avantages:
(1) Si vous l'épelez mal, vous obtenez une erreur de compilation, plutôt qu'une erreur d'exécution éventuellement subtile.
(2) Une statique peut attribuer un nom meaingful à une valeur. Ce qui est plus compréhensible:
if (employeeType.equals("R")) ...
Ou
if (employeeType.equals(EmployeeType.RETIRED)) ...
(3) Quand il y a plusieurs valeurs liées, Vous pouvez mettre un groupe de statique finale ensemble en haut du programme, informant ainsi les futurs lecteurs quelles sont toutes les valeurs possibles. J'ai eu beaucoup de fois où j'ai vu une fonction Comparer une valeur à deux ou trois littéraux. Et cela me laisse me demander: y a-t-il d'autres valeurs possibles, ou est-ce cela? (Mieux encore est souvent d'avoir une énumération, mais c'est une autre histoire.)
Tous les littéraux de chaîne sont conservés dans un cache de chaîne (ceci est dans toutes les classes)
L'utilisation d'une constante peut rendre le code plus clair, donner à la chaîne un certain contexte et rendre le code plus facile à maintenir esp si la même chaîne apparaît à plusieurs endroits.
Ces littéraux de chaîne sont internalisés, donc aucun nouvel objet de chaîne n'est créé dans la boucle. L'utilisation du même littéral deux fois pourrait toujours être un signe d'odeur de code, mais pas en termes de vitesse ou d'utilisation de la mémoire.
Dans les cas que vous fournissez, je crois que la principale raison pour l'avoir déclaré comme FINAL_STRING
quelque part est de s'assurer qu'il reste dans un emplacement centralisé. Il n'y aura qu'une seule instance de cette chaîne constante, mais le premier exemple est beaucoup plus facile à maintenir.