Estou surpreso que ninguém tenha realmente postado algum código real que seja descompilado para provar que há pelo menos alguma diferença menor.
Para a referência este foi testado contra a javac
versão 8
, 9
e 10
.
Suponha que este método:
public static int test() {
/* final */ Object left = new Object();
Object right = new Object();
return left.hashCode() + right.hashCode();
}
Compilar esse código como ele é, produz exatamente o mesmo código de byte de quando final
estaria presente ( final Object left = new Object();
).
Mas este:
public static int test() {
/* final */ int left = 11;
int right = 12;
return left + right;
}
Produz:
0: bipush 11
2: istore_0
3: bipush 12
5: istore_1
6: iload_0
7: iload_1
8: iadd
9: ireturn
Deixar final
de estar presente produz:
0: bipush 12
2: istore_1
3: bipush 11
5: iload_1
6: iadd
7: ireturn
O código é praticamente auto-explicativo, caso exista uma constante de tempo de compilação, ele será carregado diretamente na pilha de operandos (não será armazenado na matriz de variáveis locais, como o exemplo anterior faz via bipush 12; istore_0; iload_0
) - o que meio que faz sentido já que ninguém pode mudar isso.
Por outro lado, por que no segundo caso o compilador não produz istore_0 ... iload_0
está além de mim, não é como se esse slot 0
fosse usado de alguma forma (poderia encolher a matriz de variáveis dessa maneira, mas pode estar faltando alguns detalhes internos, não é possível diga com certeza)
Fiquei surpreso ao ver essa otimização, considerando o quanto os pequenos javac
fazem. Como devemos sempre usarfinal
? Eu nem vou escrever um JMH
teste (o que eu queria inicialmente), tenho certeza de que o diff está na ordem de ns
(se possível, pode ser capturado). O único lugar em que isso pode ser um problema é quando um método não pode ser incorporado devido ao seu tamanho (e a declaração final
reduziria esse tamanho em alguns bytes).
Há mais dois final
s que precisam ser abordados. Primeiro, quando um método é final
(de uma JIT
perspectiva), esse método é monomórfico - e estes são os mais amados pelo JVM
.
Depois, existem final
variáveis de instância (que devem ser definidas em todos os construtores); estes são importantes, pois garantirão uma referência publicada corretamente, conforme tocado um pouco aqui e também especificado exatamente pelo JLS
.