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 javacwersji 8, 9i 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 finalw 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_0jest poza mną, to nie jest tak, że ten slot 0jest 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ć JMHtestu (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 finalzmniejszy ten rozmiar o kilka bajtów).
Do rozwiązania są jeszcze dwa kolejne final. Po pierwsze, gdy metoda jest final(z JITperspektywy), taka metoda jest monomorficzna - i są to najbardziej ukochane przez JVM.
Następnie są finalzmienne 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.