En Java, nous voyons beaucoup d'endroits où le final
mot - clé peut être utilisé mais son utilisation est rare.
Par exemple:
String str = "abc";
System.out.println(str);
Dans le cas ci-dessus, cela str
peut être le cas, final
mais cela est généralement omis.
Lorsqu'une méthode ne sera jamais remplacée, nous pouvons utiliser le mot-clé final. De même dans le cas d'une classe qui ne va pas être héritée.
L'utilisation du mot-clé final dans l'un ou l'ensemble de ces cas améliore-t-elle vraiment les performances? Si oui, alors comment? S'il vous plaît, expliquez. Si la bonne utilisation de final
importe vraiment pour les performances, quelles habitudes un programmeur Java doit-il développer pour utiliser au mieux le mot-clé?
Réponses:
Habituellement non. Pour des méthodes virtuelles, HotSpot garde la trace de savoir si la méthode a effectivement été surchargée, et est en mesure d'effectuer des optimisations telles que inline sur l' hypothèse qu'une méthode n'a pas été remplacé - jusqu'à ce qu'il charge une classe qui remplace la méthode, à quel point il peut annuler (ou annuler partiellement) ces optimisations.
(Bien sûr, cela suppose que vous utilisez HotSpot - mais c'est de loin la JVM la plus courante, donc ...)
À mon avis, vous devez utiliser
final
une conception et une lisibilité claires plutôt que pour des raisons de performances. Si vous souhaitez modifier quoi que ce soit pour des raisons de performances, vous devez effectuer les mesures appropriées avant de déformer le code le plus clair - de cette façon, vous pouvez décider si les performances supplémentaires obtenues valent la moins bonne lisibilité / conception. (D'après mon expérience, cela ne vaut presque jamais la peine; YMMV.)EDIT: Comme les champs finaux ont été mentionnés, il vaut la peine de mentionner qu'ils sont de toute façon souvent une bonne idée, en termes de conception claire. Ils modifient également le comportement garanti en termes de visibilité entre les threads: une fois qu'un constructeur a terminé, tous les champs finaux sont garantis visibles immédiatement dans d'autres threads. C'est probablement l'utilisation la plus courante de
final
mon expérience, bien qu'en tant que partisan de la règle de base de Josh Bloch "concevoir pour l'héritage ou l'interdire", je devrais probablement l'utiliserfinal
plus souvent pour les cours ...la source
final
est généralement recommandé car il rend le code plus facile à comprendre et aide à trouver les bogues (car il rend l'intention des programmeurs explicite). PMD recommande probablement d'utiliser enfinal
raison de ces problèmes de style, et non pour des raisons de performances.Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.
. En outreAn immutable object can be in exactly one state, the state in which it was created.
vsMutable objects, on the other hand, can have arbitrarily complex state spaces.
. D'après mon expérience personnelle, l'utilisation du mot-cléfinal
devrait mettre en évidence l'intention du développeur de tendre vers l'immuabilité, et non "d'optimiser" le code. Je vous encourage à lire ce chapitre, fascinant!final
mot - clé sur les variables pourrait réduire la quantité de bytecode, ce qui pourrait produire un effet sur les performances.Réponse courte: ne vous en faites pas!
Longue réponse:
Lorsque vous parlez de variables locales finales, gardez à l'esprit que l'utilisation du mot-clé
final
aidera le compilateur à optimiser statiquement le code , ce qui peut finalement aboutir à un code plus rapide. Par exemple, les chaînes finalesa + b
de l'exemple ci-dessous sont concaténées statiquement (au moment de la compilation).Le résultat?
Testé sur Java Hotspot VM 1.7.0_45-b18.
Alors, quelle est l'amélioration réelle des performances? Je n'ose pas dire. Dans la plupart des cas, probablement marginaux (~ 270 nanosecondes dans ce test synthétique car la concaténation de chaînes est totalement évitée - un cas rare), mais dans un code utilitaire hautement optimisé, cela pourrait être un facteur. Dans tous les cas, la réponse à la question d'origine est oui, cela pourrait améliorer les performances, mais au mieux marginalement .
Mis à part les avantages à la compilation, je n'ai trouvé aucune preuve que l'utilisation du mot clé
final
ait un effet mesurable sur les performances.la source
String
la concaténation est une opération beaucoup plus coûteuse que l'ajoutIntegers
. Le faire statiquement (si possible) est plus efficace, c'est ce que montre le test.Oui il peut. Voici un exemple où final peut augmenter les performances:
La compilation conditionnelle est une technique dans laquelle les lignes de code ne sont pas compilées dans le fichier de classe en fonction d'une condition particulière. Cela peut être utilisé pour supprimer des tonnes de code de débogage dans une version de production.
considérer ce qui suit:
En convertissant l'attribut doSomething en attribut final, vous avez indiqué au compilateur que chaque fois qu'il voit doSomething, il doit le remplacer par false conformément aux règles de substitution au moment de la compilation. La première passe du compilateur change le code en quelque chose comme ceci:
Une fois cela fait, le compilateur l'examine de nouveau et voit qu'il y a des instructions inaccessibles dans le code. Puisque vous travaillez avec un compilateur de qualité supérieure, il n'aime pas tous ces codes d'octets inaccessibles. Donc, cela les supprime, et vous vous retrouvez avec ceci:
réduisant ainsi tout code excessif ou toute vérification conditionnelle inutile.
Modifier: à titre d'exemple, prenons le code suivant:
Lors de la compilation de ce code avec Java 8 et de la décompilation avec
javap -c Test.class
nous obtenons:Nous pouvons noter que le code compilé comprend uniquement la variable non finale
x
. Cela prouve que les variables finales ont un impact sur les performances, au moins pour ce cas simple.la source
Selon IBM - ce n'est pas le cas pour les classes ou les méthodes.
http://www.ibm.com/developerworks/java/library/j-jtp04223.html
la source
Je suis étonné que personne n'ait réellement publié de code réel décompilé pour prouver qu'il y a au moins quelques différences mineures.
Pour référence, cela a été testé par rapport à la
javac
version8
,9
et10
.Supposons que cette méthode:
Compiler ce code tel qu'il est, produit le exacte même code d'octets que quand
final
aurait été présent (final Object left = new Object();
).Mais celui-ci:
Produit:
Le fait
final
d'être présent produit:Le code est assez explicite, dans le cas où il y a une constante de temps de compilation, il sera chargé directement sur la pile d'opérandes (il ne sera pas stocké dans le tableau de variables locales comme l'exemple précédent le fait via
bipush 12; istore_0; iload_0
) - ce qui a du sens puisque personne ne peut le changer.D'un autre côté, pourquoi dans le deuxième cas le compilateur ne produit
istore_0 ... iload_0
pas me dépasse, ce n'est pas comme si cet emplacement0
était utilisé de quelque manière que ce soit (cela pourrait réduire le tableau de variables de cette façon, mais il peut y avoir des détails internes manquants, je ne peux pas dire à coup sûr)J'ai été surpris de voir une telle optimisation, compte tenu du fait que les plus petits le
javac
font. Quant à devrions-nous toujours utiliserfinal
? Je ne vais même pas écrire unJMH
test (que je voulais au départ), je suis sûr que le diff est de l'ordre dens
(si possible à capturer du tout). Le seul endroit où cela pourrait être un problème, c'est quand une méthode n'a pas pu être insérée à cause de sa taille (et déclarerfinal
réduirait cette taille de quelques octets).Il y a deux autres
final
points qui doivent être résolus. Tout d'abord, lorsqu'une méthode estfinal
(d'unJIT
point de vue), une telle méthode est monomorphe - et ce sont les plus appréciées par leJVM
.Ensuite, il y a les
final
variables d'instance (qui doivent être définies dans chaque constructeur); ceux-ci sont importants car ils garantiront une référence correctement publiée, comme touché un peu ici et également spécifié exactement par leJLS
.la source
javac -g FinalTest.java
), décompilé le code en utilisantjavap -c FinalTest.class
et n'ai pas obtenu les mêmes résultats (avecfinal int left=12
, j'ai eubipush 11; istore_0; bipush 12; istore_1; bipush 11; iload_1; iadd; ireturn
). Alors oui, le bytecode généré dépend de nombreux facteurs et il est difficile de dire si cela afinal
ou non un effet sur les performances. Mais comme le bytecode est différent, une différence de performance peut exister.Vous posez vraiment des questions sur deux (au moins) cas différents:
final
pour les variables localesfinal
pour les méthodes / classesJon Skeet a déjà répondu 2). Environ 1):
Je ne pense pas que cela fasse une différence; pour les variables locales, le compilateur peut déduire si la variable est finale ou non (simplement en vérifiant si elle est affectée plusieurs fois). Donc, si le compilateur souhaite optimiser les variables qui ne sont attribuées qu'une seule fois, il peut le faire, que la variable soit réellement déclarée
final
ou non.final
pourrait faire une différence pour les champs de classe protégés / publics; là, il est très difficile pour le compilateur de savoir si le champ est défini plusieurs fois, car cela peut arriver à partir d'une classe différente (qui peut même ne pas avoir été chargée). Mais même dans ce cas, la JVM pourrait utiliser la technique décrite par Jon (optimiser de manière optimiste, revenir en arrière si une classe est chargée, ce qui change le champ).En résumé, je ne vois aucune raison pour laquelle cela devrait améliorer les performances. Il est donc peu probable que ce type de micro-optimisation aide. Vous pouvez essayer de le comparer pour vous en assurer, mais je doute que cela fasse une différence.
Éditer:
En fait, selon la réponse de Timo Westkämper, dans certains cas , les performances des champs de classe
final
peuvent être améliorées. Je me suis trompé.la source
final
, pour vous assurer de ne pas l'attribuer deux fois. Pour autant que je puisse voir, la même logique de vérification peut être (et est probablement) utilisée pour vérifier si une variable pourrait l'êtrefinal
.javac
à mieux l'optimiser. Mais ce n'est que de la spéculation.final
variable est d'un type ou d'un type primitifString
et est immédiatement affectée d'une constante de compilation comme dans l'exemple de la question, le compilateur doit l' inclure, car la variable est une constante de compilation par spécification. Mais pour la plupart des cas d'utilisation, le code peut sembler différent, mais cela ne fait toujours aucune différence que la constante soit en ligne ou lue à partir d'une variable locale en termes de performances.Remarque: pas un expert java
Si je me souviens bien de ma java, il y aurait très peu de moyen d'améliorer les performances en utilisant le mot-clé final. J'ai toujours su qu'il existait pour le «bon code» - conception et lisibilité.
la source
Je ne suis pas un expert, mais je suppose que vous devez ajouter un
final
mot clé à la classe ou à la méthode s'il ne sera pas écrasé et laisser les variables seules. S'il existe un moyen d'optimiser de telles choses, le compilateur le fera pour vous.la source
En fait, en testant du code lié à OpenGL, j'ai trouvé que l'utilisation du dernier modificateur sur un champ privé pouvait dégrader les performances. Voici le début de la classe que j'ai testée:
Et c'est la méthode que j'ai utilisée pour tester les performances de diverses alternatives, parmi lesquelles la classe ShaderInput:
Après la 3e itération, avec la VM réchauffée, j'ai toujours obtenu ces chiffres sans le mot clé final:
Avec le mot-clé final:
Notez les chiffres du test ShaderInput.
Peu importait que je rende les champs publics ou privés.
Soit dit en passant, il y a quelques autres choses déconcertantes. La classe ShaderInput surpasse toutes les autres variantes, même avec le mot-clé final. C'est remarquable car il s'agit essentiellement d'une classe enveloppant un tableau flottant, tandis que les autres tests manipulent directement le tableau. Je dois comprendre celui-ci. Peut avoir quelque chose à voir avec l'interface fluide de ShaderInput.
De plus, System.arrayCopy est apparemment plus lent pour les petits tableaux que de simplement copier des éléments d'un tableau à l'autre dans une boucle for. Et l'utilisation de sun.misc.Unsafe (ainsi que de java.nio.FloatBuffer direct, non illustré ici) fonctionne de façon catastrophique.
la source
La finale (au moins pour les variables et les paramètres des membres) est plus pour les humains que pour la machine.
Il est recommandé de rendre les variables définitives dans la mesure du possible. Je souhaite que Java ait rendu les "variables" finales par défaut et avait un mot-clé "Mutable" pour autoriser les changements. Les classes immuables conduisent à un code threadé bien meilleur, et le simple fait de jeter un coup d'œil à une classe avec "final" devant chaque membre montrera rapidement qu'elle est immuable.
Un autre cas - j'ai converti beaucoup de code pour utiliser les annotations @ NonNull / @ Nullable (Vous pouvez dire qu'un paramètre de méthode ne doit pas être nul alors l'IDE peut vous avertir chaque fois que vous passez une variable qui n'est pas étiquetée @ Non nul - le tout se propage à un degré ridicule). Il est beaucoup plus facile de prouver qu'une variable ou un paramètre membre ne peut pas être nul lorsqu'il est marqué comme final, car vous savez qu'il n'est pas réaffecté ailleurs.
Ma suggestion est de prendre l'habitude d'appliquer la finale pour les membres et les paramètres par défaut, c'est juste quelques caractères mais vous poussera à améliorer votre style de codage si rien d'autre.
Final pour les méthodes ou les classes est un autre concept car il interdit une forme de réutilisation très valide et ne dit pas grand-chose au lecteur. La meilleure utilisation est probablement la façon dont ils ont fait la chaîne et les autres types intrinsèques finaux afin que vous puissiez compter sur un comportement cohérent partout - Cela a empêché beaucoup de bugs (bien qu'il y ait des fois où j'aurais AIMÉ étendre la chaîne ... oh le possibilités)
la source
Comme mentionné ailleurs, «final» pour une variable locale, et dans une moindre mesure une variable membre, est plus une question de style.
'final' est une déclaration selon laquelle vous souhaitez que la variable ne change pas (c'est-à-dire que la variable ne variera pas!). Le compilateur peut alors vous aider en vous plaignant si vous violez votre propre contrainte.
Je partage le sentiment que Java aurait été un meilleur langage si les identifiants (je suis désolé, je ne peux pas appeler une chose non variable une `` variable '') étaient définitifs par défaut, et vous demandaient de dire explicitement qu'il s'agissait de variables. Mais cela dit, je n'utilise généralement pas «final» sur les variables locales qui sont initialisées et jamais assignées; cela semble trop bruyant.
(J'utilise final sur les variables membres)
la source
Les membres déclarés avec final seront disponibles tout au long du programme car contrairement aux non finaux, si ces membres n'ont pas été utilisés dans le programme, ils ne seront pas pris en charge par Garbage Collector, ce qui peut entraîner des problèmes de performances en raison d'une mauvaise gestion de la mémoire.
la source
final
Le mot-clé peut être utilisé de cinq façons en Java.Une classe est finale: une classe est finale signifie que nous ne pouvons pas être étendus ou que l'héritage signifie que l'héritage n'est pas possible.
De même - Un objet est final: quelque temps nous ne modifions pas l'état interne de l'objet donc dans ce cas nous pouvons spécifier que l'objet est final object.object final signifie pas variable aussi final.
Une fois la variable de référence rendue définitive, elle ne peut pas être réaffectée à un autre objet. Mais peut changer le contenu de l'objet tant que ses champs ne sont pas définitifs
la source
final
moyens, mais si l'utilisationfinal
influençait les performances.