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
version 8
, 9
et 10
.
Supposons que cette méthode:
public static int test() {
/* final */ Object left = new Object();
Object right = new Object();
return left.hashCode() + right.hashCode();
}
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:
public static int test() {
/* final */ int left = 11;
int right = 12;
return left + right;
}
Produit:
0: bipush 11
2: istore_0
3: bipush 12
5: istore_1
6: iload_0
7: iload_1
8: iadd
9: ireturn
Le fait final
d'être présent produit:
0: bipush 12
2: istore_1
3: bipush 11
5: iload_1
6: iadd
7: ireturn
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 emplacement 0
é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 utiliser final
? Je ne vais même pas écrire un JMH
test (que je voulais au départ), je suis sûr que le diff est de l'ordre de ns
(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 est final
(d'un JIT
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 le JLS
.