Dziwię się, że nikt tak naprawdę nie opublikował prawdziwego kodu, który jest dekompilowany, aby udowodnić, że istnieje co najmniej niewielka różnica.
Na odniesienie to zostało przetestowane na javac
wersji 8
, 9
i 10
.
Załóżmy, że ta metoda:
public static int test() {
/* final */ Object left = new Object();
Object right = new Object();
return left.hashCode() + right.hashCode();
}
Kompilacja tego kodu w obecnej postaci tworzy dokładnie taki sam kod bajtu, jak w przypadku final
, gdy byłby obecny (final Object left = new Object();
).
Ale ten:
public static int test() {
/* final */ int left = 11;
int right = 12;
return left + right;
}
Produkuje:
0: bipush 11
2: istore_0
3: bipush 12
5: istore_1
6: iload_0
7: iload_1
8: iadd
9: ireturn
Pozostawanie final
w obecności powoduje:
0: bipush 12
2: istore_1
3: bipush 11
5: iload_1
6: iadd
7: ireturn
Kod jest dość oczywisty, w przypadku gdy istnieje stała czasowa kompilacji, zostanie załadowana bezpośrednio na stos operandów (nie będzie przechowywana w tablicy zmiennych lokalnych, jak w poprzednim przykładzie bipush 12; istore_0; iload_0
) - co to ma sens ponieważ nikt nie może tego zmienić.
Z drugiej strony, dlaczego w drugim przypadku kompilator nie produkuje istore_0 ... iload_0
jest poza mną, to nie jest tak, że ten slot 0
jest używany w jakikolwiek sposób (może zmniejszać tablicę zmiennych w ten sposób, ale może brakować szczegółów wewnętrznych, nie mogę powiedz na pewno)
Byłem zaskoczony widząc taką optymalizację, biorąc pod uwagę, jak małe są javac
. Co powinniśmy zawsze używać final
? Nie zamierzam nawet pisać JMH
testu (który chciałem początkowo), jestem pewien, że diff jest w kolejności ns
(jeśli w ogóle można go przechwycić). Jedynym miejscem, w którym może to być problem, jest to, że metoda nie może zostać wstawiona ze względu na jej rozmiar (i zadeklarowanie final
zmniejszy ten rozmiar o kilka bajtów).
Do rozwiązania są jeszcze dwa kolejne final
. Po pierwsze, gdy metoda jest final
(z JIT
perspektywy), taka metoda jest monomorficzna - i są to najbardziej ukochane przez JVM
.
Następnie są final
zmienne instancji (które muszą być ustawione w każdym konstruktorze); są one ważne, ponieważ zagwarantują poprawne opublikowanie odniesienia, które zostało tu nieco zmienione, a także dokładnie określone przez JLS
.