Czy użycie końcowego słowa kluczowego w Javie poprawia wydajność?

347

W Javie widzimy wiele miejsc, w których finalmożna użyć słowa kluczowego, ale jego użycie jest rzadkie.

Na przykład:

String str = "abc";
System.out.println(str);

W powyższym przypadku strmoże być, finalale jest to zwykle pomijane.

Gdy metoda nigdy nie zostanie zastąpiona, możemy użyć końcowego słowa kluczowego. Podobnie w przypadku klasy, która nie będzie dziedziczona.

Czy użycie końcowego słowa kluczowego w którymkolwiek z powyższych przypadków naprawdę poprawia wydajność? Jeśli tak, to jak? Proszę wytłumacz. Jeśli właściwe użycie finalnaprawdę ma znaczenie dla wydajności, jakie nawyki powinien rozwinąć programista Java, aby jak najlepiej wykorzystać słowo kluczowe?

Abhishek Jain
źródło
Nie sądzę, że tak, wysyłanie metod (buforowanie strony wywołań i ...) jest problemem w językach dynamicznych, a nie w językach statycznych
Jahan
Jeśli uruchomię narzędzie PMD (wtyczka do środowiska Eclipse) używane do sprawdzania celu, sugeruję wprowadzenie zmian dla zmiennej w przypadku, jak pokazano powyżej. Ale nie zrozumiałem jego koncepcji. Naprawdę wydajność uderza tak bardzo?
Abhishek Jain
5
Myślałem, że to typowe pytanie egzaminacyjne. Pamiętam, że końcowy ma mieć wpływ na wydajność, IIRC Klasy końcowe mogą być optymalizowane przez JRE w jakiś sposób, ponieważ nie mogą być podklasy.
Kawu,
Właściwie to to przetestowałem. Na wszystkich JVMs testowałem wykorzystanie końcowy na zmienne lokalne nie poprawić wydajność (nieznacznie, ale jednak może być czynnikiem metod użytkowych). Kod źródłowy znajduje się w mojej odpowiedzi poniżej.
rustyx
1
„przedwczesna optymalizacja jest źródłem wszelkiego zła”. Po prostu pozwól kompilatorowi wykonać swoją pracę. Napisz czytelny i dobrze skomentowany kod. To zawsze najlepszy wybór!
kaiser,

Odpowiedzi:

285

Zwykle nie. W przypadku metod wirtualnych HotSpot śledzi, czy metoda została faktycznie zastąpiona, i jest w stanie przeprowadzić optymalizacje, takie jak inklinacja przy założeniu, że metoda nie została zastąpiona - dopóki nie załaduje klasy, która przesłania metodę, w którym to momencie może cofnąć (lub częściowo cofnąć) te optymalizacje.

(Oczywiście zakładamy, że używasz HotSpot - ale jest to zdecydowanie najpopularniejsza maszyna JVM, więc ...)

Moim zdaniem powinieneś używać w finaloparciu o przejrzysty projekt i czytelność, a nie ze względu na wydajność. Jeśli chcesz coś zmienić ze względu na wydajność, powinieneś wykonać odpowiednie pomiary przed zgięciem najczystszego kodu z kształtu - w ten sposób możesz zdecydować, czy jakakolwiek dodatkowa wydajność jest warta gorszej czytelności / projektu. (Z mojego doświadczenia wynika, że ​​prawie nigdy nie warto; YMMV.)

EDYCJA: Jak już wspomniano o ostatnich polach, warto wspomnieć, że i tak często są dobrym pomysłem, jeśli chodzi o przejrzysty design. Zmieniają również gwarantowane zachowanie pod względem widoczności wątków: po zakończeniu działania konstruktora wszelkie pola końcowe są natychmiast widoczne w innych wątkach. Jest to prawdopodobnie najczęstsze zastosowanie finalw moim doświadczeniu, chociaż jako zwolennik praktycznej reguły Josha Blocha dotyczącej „dziedziczenia lub zakazania”, prawdopodobnie powinienem finalczęściej korzystać z zajęć ...

Jon Skeet
źródło
1
@Abhishek: O czym konkretnie? Najważniejszy jest ostatni - że prawie na pewno nie powinieneś się o to martwić.
Jon Skeet,
9
@Abishek: finaljest generalnie zalecane, ponieważ ułatwia zrozumienie kodu i pomaga znaleźć błędy (ponieważ sprawia, że ​​intencje programistów są wyraźne). PMD prawdopodobnie zaleca używanie z finalpowodu tych problemów ze stylem, a nie ze względu na wydajność.
sleske,
3
@Abhishek: Wiele z nich prawdopodobnie jest specyficznych dla JVM i może polegać na bardzo subtelnych aspektach kontekstu. Na przykład, uważam, że JVM serwera HotSpot nadal pozwoli na wprowadzanie metod wirtualnych, gdy zostanie zastąpione w jednej klasie, z szybką weryfikacją typu, jeśli to właściwe. Ale szczegóły są trudne do ustalenia i mogą się zmieniać między wydaniem.
Jon Skeet,
5
Tu chciałbym zacytować Effective Java, 2nd Edition, pkt 15, zminimalizować zmienność: Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.. Ponadto An immutable object can be in exactly one state, the state in which it was created.vs Mutable objects, on the other hand, can have arbitrarily complex state spaces.. Z własnego doświadczenia wynika, że ​​użycie słowa kluczowego finalpowinno uwypuklić intencję programisty, by skłaniać się ku niezmienności, a nie „optymalizować” kod. Zachęcam do przeczytania tego rozdziału, fascynującego!
Louis F.
2
Inne odpowiedzi pokazują, że użycie finalsłowa kluczowego w zmiennych może zmniejszyć ilość kodu bajtowego, co może mieć wpływ na wydajność.
Julien Kronegg
86

Krótka odpowiedź: nie martw się!

Długa odpowiedź:

Mówiąc o końcowych zmiennych lokalnych, należy pamiętać, że użycie słowa kluczowego finalpomoże kompilatorowi zoptymalizować kod statycznie , co może w efekcie doprowadzić do szybszego kodu. Na przykład końcowe łańcuchy a + bw poniższym przykładzie są łączone statycznie (w czasie kompilacji).

public class FinalTest {

    public static final int N_ITERATIONS = 1000000;

    public static String testFinal() {
        final String a = "a";
        final String b = "b";
        return a + b;
    }

    public static String testNonFinal() {
        String a = "a";
        String b = "b";
        return a + b;
    }

    public static void main(String[] args) {
        long tStart, tElapsed;

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method with finals took " + tElapsed + " ms");

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testNonFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method without finals took " + tElapsed + " ms");

    }

}

Wynik?

Method with finals took 5 ms
Method without finals took 273 ms

Testowane na Java Hotspot VM 1.7.0_45-b18.

Ile wynosi faktyczna poprawa wydajności? Nie śmiem powiedzieć. W większości przypadków prawdopodobnie marginalny (~ 270 nanosekund w tym teście syntetycznym, ponieważ w ogóle unika się konkatenacji łańcucha - rzadki przypadek), ale w wysoce zoptymalizowanym kodzie użytecznym może to być czynnik. W każdym razie odpowiedź na pierwotne pytanie brzmi „ tak”, może poprawić wydajność, ale w najlepszym razie nieznacznie .

Pomijając korzyści z czasu kompilacji, nie mogłem znaleźć żadnych dowodów na to, że użycie słowa kluczowego finalma jakikolwiek wymierny wpływ na wydajność.

rustyx
źródło
2
Przepisałem nieco twój kod, aby przetestować oba przypadki 100 razy. Ostatecznie średni czas finału wyniósł 0 ms i 9 ms w przypadku finału. Zwiększenie liczby iteracji do 10 M ustawia średnią na 0 ms i 75 ms. Najlepszym przebiegiem w niefinałowym było jednak 0 ms. Być może wyniki wykrywania maszyn wirtualnych są po prostu wyrzucane czy coś takiego? Nie wiem, ale mimo to użycie finału robi znaczącą różnicę.
Casper Færgemand
5
Wadliwy test. Wcześniejsze testy rozgrzeją JVM i przyniosą korzyści późniejszym wywołaniom testowym. Zmień kolejność testów i zobacz, co się stanie. Musisz uruchomić każdy test we własnej instancji JVM.
Steve Kuo,
16
Nie, test nie jest wadliwy, uwzględniono rozgrzewkę. Drugi test jest wolniejszy, a nie szybszy. Bez rozgrzewki drugi test byłby NAWET WOLNIEJSZY.
rustyx
6
W testFinal () cały czas zwracał ten sam obiekt, z puli łańcuchów, ponieważ odporność końcowych łańcuchów i konkatenacji literałów łańcuchów są oceniane w czasie kompilacji. TestNonFinal () za każdym razem, gdy zwraca nowy obiekt, wyjaśnia różnicę prędkości.
anber
4
Co sprawia, że ​​uważasz, że ten scenariusz jest nierealny? Stringkonkatenacja jest znacznie bardziej kosztowną operacją niż dodawanie Integers. Robienie tego statycznie (jeśli to możliwe) jest bardziej wydajne, tak pokazuje test.
rustyx
62

Tak, może. Oto przykład, w którym finał może zwiększyć wydajność:

Kompilacja warunkowa to technika, w której wiersze kodu nie są kompilowane do pliku klasy na podstawie określonego warunku. Można tego użyć do usunięcia ton kodu debugowania w kompilacji produkcyjnej.

rozważ następujące:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (doSomething) {
       // do first part. 
    }

    if (doSomething) {
     // do second part. 
    }

    if (doSomething) {     
      // do third part. 
    }

    if (doSomething) {
    // do finalization part. 
    }
}

Konwertując atrybut doSomething na atrybut końcowy, powiedziałeś kompilatorowi, że ilekroć zobaczy on doSomething, powinien zastąpić go wartością false zgodnie z regułami podstawiania w czasie kompilacji. Pierwsze przejście kompilatora zmienia kod na mniej więcej taki:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (false){
       // do first part. 
    }

    if (false){
     // do second part. 
    }

    if (false){
      // do third part. 
    }

    if (false){
    // do finalization part. 

    }
}

Po wykonaniu tej czynności kompilator przyjrzy się jej ponownie i zobaczy, że w kodzie znajdują się nieosiągalne instrukcje. Ponieważ pracujesz z kompilatorem najwyższej jakości, nie lubi wszystkich tych nieosiągalnych kodów bajtów. Usuwa je i kończy się na tym:

public class ConditionalCompile {


  private final static boolean doSomething= false;

  public static void someMethodBetter( ) {

    // do first part. 

    // do second part. 

    // do third part. 

    // do finalization part. 

  }
}

zmniejszając w ten sposób wszelkie nadmierne kody lub niepotrzebne sprawdzanie warunkowe.

Edycja: Jako przykład weźmy następujący kod:

public class Test {
    public static final void main(String[] args) {
        boolean x = false;
        if (x) {
            System.out.println("x");
        }
        final boolean y = false;
        if (y) {
            System.out.println("y");
        }
        if (false) {
            System.out.println("z");
        }
    }
}

Podczas kompilacji tego kodu w Javie 8 i dekompilacji javap -c Test.classotrzymujemy:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static final void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #22                 // String x
      11: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iconst_0
      15: istore_2
      16: return
}

Możemy zauważyć, że skompilowany kod zawiera tylko nie-końcową zmienną x. Dowodzi to, że zmienne końcowe mają wpływ na wyniki, przynajmniej w tym prostym przypadku.

mel3kings
źródło
1
@ ŁukaszLech Nauczyłem się tego z książki Oreilly: Hardcore Java, w ich rozdziale dotyczącym końcowego słowa kluczowego.
mel3kings
15
Mówi o optymalizacji w czasie kompilacji, oznacza to, że programista zna WARTOŚĆ końcowej zmiennej boolowskiej w czasie kompilacji, jaki jest sens pisania, jeśli bloki są na pierwszym miejscu, to w tym scenariuszu, w którym WARUNKI IF nie są konieczne i nie czyni jakiś sens? Moim zdaniem, nawet jeśli poprawia to wydajność, jest to przede wszystkim zły kod i może być zoptymalizowany przez samego programistę, zamiast przekazywać odpowiedzialność kompilatorowi, a pytanie dotyczy głównie poprawy wydajności zwykłych kodów, w których używany jest finał, który ma sens programowy.
Bhavesh Agarwal
7
Chodzi o to, aby dodać instrukcje debugowania, jak stwierdzono w mel3kings. Możesz przerzucić zmienną przed kompilacją produkcyjną (lub skonfigurować ją w skryptach kompilacji) i automatycznie usunąć cały ten kod po utworzeniu dystrybucji.
Adam
16

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.

Eugene
źródło
Skompilowałem kod za pomocą Java 8 (JDK 1.8.0_162) z opcjami debugowania ( javac -g FinalTest.java), zdekompilowałem kod za pomocą javap -c FinalTest.classi nie final int left=12uzyskałem tych samych wyników (z , mam bipush 11; istore_0; bipush 12; istore_1; bipush 11; iload_1; iadd; ireturn). Tak więc, wygenerowany kod bajtowy zależy od wielu czynników i trudno powiedzieć, czy ma finalwpływ na wydajność, czy nie. Ale ponieważ kod bajtowy jest inny, mogą występować pewne różnice w wydajności.
Julien Kronegg
1
Czy to nie jest
zdekompilowany
13

Naprawdę pytasz o dwa (przynajmniej) różne przypadki:

  1. final dla zmiennych lokalnych
  2. final dla metod / klas

Jon Skeet już odpowiedział 2). O 1):

Nie sądzę, żeby miało to znaczenie; w przypadku zmiennych lokalnych kompilator może wywnioskować, czy zmienna jest ostateczna, czy nie (po prostu sprawdzając, czy przypisano ją więcej niż jeden raz). Jeśli więc kompilator chciał zoptymalizować zmienne przypisane tylko raz, może to zrobić bez względu na to, czy zmienna jest faktycznie zadeklarowana, finalczy nie.

final moc mieć znaczenie dla pól klasy chronionej / publicznej; kompilatorowi bardzo trudno jest dowiedzieć się, czy pole jest ustawiane więcej niż jeden raz, ponieważ mogłoby się to zdarzyć z innej klasy (która mogła nawet nie zostać załadowana). Ale nawet wtedy JVM może skorzystać z techniki opisanej przez Jona (optymalizować optymistycznie, cofnąć, jeśli zostanie załadowana klasa, która zmienia pole).

Podsumowując, nie widzę żadnego powodu, dla którego powinien on poprawić wydajność. Ten rodzaj mikrooptymalizacji raczej nie pomoże. Możesz spróbować go przetestować, aby się upewnić, ale wątpię, czy to coś zmieni.

Edytować:

W rzeczywistości, zgodnie z odpowiedzią Timo Westkämper, final może poprawić wydajność pól klasowych w niektórych przypadkach. Poprawiono mnie.

Śleske
źródło
Nie sądzę, aby kompilator mógł poprawnie sprawdzić liczbę przypisań zmiennej lokalnej: co powiesz na struktury if-then-else z licznymi przypisaniami?
gyorgyabraham
1
@gyabraham: Kompilator już sprawdza te przypadki, jeśli deklarujesz zmienną lokalną jako final, aby upewnić się, że nie przypisujesz jej dwukrotnie. O ile widzę, ta sama logika sprawdzania może być (i prawdopodobnie jest) używana do sprawdzania, czy zmienna może być final.
śleske,
Ostateczność zmiennej lokalnej nie jest wyrażona w kodzie bajtowym, dlatego JVM nawet nie wie, że była ona ostateczna
Steve Kuo
1
@ SteveKuo: Nawet jeśli nie jest wyrażony w kodzie bajtowym, może pomóc javacw lepszej optymalizacji. Ale to tylko spekulacje.
sleske
1
Kompilator może dowiedzieć się, czy zmienna lokalna została przypisana dokładnie raz, ale w praktyce tak nie jest (poza sprawdzaniem błędów). Z drugiej strony, jeśli finalzmienna jest pierwotnym typem lub typem Stringi jest natychmiast przypisywana ze stałą czasową kompilacji, jak w przykładzie pytania, kompilator musi ją wstawić, ponieważ zmienna jest stałą czasową kompilacji dla specyfikacji. Jednak w większości przypadków kod może wyglądać inaczej, ale nadal nie ma znaczenia, czy stała jest wstawiana, czy odczytywana ze zmiennej lokalnej pod względem wydajności.
Holger
6

Uwaga: nie jestem ekspertem od Java

Jeśli dobrze pamiętam moją Javę, nie byłoby sposobu na poprawienie wydajności przy użyciu końcowego słowa kluczowego. Zawsze wiedziałem, że istnieje dla „dobrego kodu” - projektu i czytelności.

Neowizard
źródło
1

Nie jestem ekspertem, ale przypuszczam, że powinieneś dodać finalsłowo kluczowe do klasy lub metody, jeśli nie zostanie ona nadpisana i pozostaw zmienne w spokoju. Jeśli będzie jakiś sposób na optymalizację takich rzeczy, kompilator zrobi to za Ciebie.

Crozin
źródło
1

Właściwie podczas testowania kodu związanego z OpenGL odkryłem, że użycie końcowego modyfikatora w polu prywatnym może obniżyć wydajność. Oto początek klasy, którą testowałem:

public class ShaderInput {

    private /* final */ float[] input;
    private /* final */ int[] strides;


    public ShaderInput()
    {
        this.input = new float[10];
        this.strides = new int[] { 0, 4, 8 };
    }


    public ShaderInput x(int stride, float val)
    {
        input[strides[stride] + 0] = val;
        return this;
    }

    // more stuff ...

I to jest metoda, której użyłem do przetestowania wydajności różnych alternatyw, między innymi klasy ShaderInput:

public static void test4()
{
    int arraySize = 10;
    float[] fb = new float[arraySize];
    for (int i = 0; i < arraySize; i++) {
        fb[i] = random.nextFloat();
    }
    int times = 1000000000;
    for (int i = 0; i < 10; ++i) {
        floatVectorTest(times, fb);
        arrayCopyTest(times, fb);
        shaderInputTest(times, fb);
        directFloatArrayTest(times, fb);
        System.out.println();
        System.gc();
    }
}

Po trzeciej iteracji z rozgrzaną maszyną wirtualną konsekwentnie otrzymywałem te liczby bez ostatniego słowa kluczowego:

Simple array copy took   : 02.64
System.arrayCopy took    : 03.20
ShaderInput took         : 00.77
Unsafe float array took  : 05.47

Z końcowym słowem kluczowym:

Simple array copy took   : 02.66
System.arrayCopy took    : 03.20
ShaderInput took         : 02.59
Unsafe float array took  : 06.24

Zwróć uwagę na liczby dla testu ShaderInput.

Nie miało znaczenia, czy upubliczniłem pola.

Nawiasem mówiąc, jest jeszcze kilka dziwnych rzeczy. Klasa ShaderInput przewyższa wszystkie inne warianty, nawet w przypadku ostatniego słowa kluczowego. Jest to niezwykłe, ponieważ zasadniczo jest to klasa owijająca tablicę zmiennoprzecinkową, podczas gdy inne testy bezpośrednio manipulują. Muszę to rozgryźć. Może mieć coś wspólnego z płynnym interfejsem ShaderInput.

Również System.arrayCopy jest najwyraźniej nieco wolniejszy dla małych tablic niż zwykłe kopiowanie elementów z jednej tablicy do drugiej w pętli for. A użycie sun.misc.Unsafe (a także bezpośredniego java.nio.FloatBuffer, niepokazanego tutaj) wykonuje się niesamowicie.

użytkownik3663845
źródło
1
Zapomniałeś, aby parametry były ostateczne. <pre> <code> public ShaderInput x (final int stride, final float val) {input [strides [stride] + 0] = val; zwróć to; } </code> </pre> Z moich doświadczeń wynika, że ​​wykonanie dowolnej zmiennej lub finału w polu może rzeczywiście zwiększyć wydajność.
Anticro
1
Aha, a także spraw, by pozostałe też były ostateczne: <pre> <code> final int arraySize = 10; final float [] fb = new float [arraySize]; for (int i = 0; i <rozmiar tablicy; i ++) {fb [i] = random.nextFloat (); } końcowe czasy int = 1000000000; for (int i = 0; i <10; ++ i) {floatVectorTest (times, fb); arrayCopyTest (times, fb); shaderInputTest (times, fb); directFloatArrayTest (times, fb); System.out.println (); System.gc (); } </code> </pre>
Anticro
1

Ostateczne (przynajmniej dla zmiennych i parametrów składowych) jest bardziej dla ludzi niż dla maszyny.

Dobrą praktyką jest, aby zmienne były ostateczne tam, gdzie to możliwe. Chciałbym, aby Java domyślnie uczyniła „zmiennymi” ostatecznymi i zawierała słowo kluczowe „Zmienne”, aby umożliwić zmiany. Niezmienne klasy prowadzą do znacznie lepszego kodu wątkowego, a samo spojrzenie na klasę z „końcowym” przed każdym elementem szybko pokaże, że jest niezmienna.

Kolejny przypadek - konwertowałem dużo kodu, aby używać adnotacji @ NonNull / @ Nullable (możesz powiedzieć, że parametr metody nie może mieć wartości null, wówczas IDE może ostrzec Cię o każdym miejscu, w którym przekazujesz zmienną, która nie jest oznaczona @ NonNull - cała sprawa rozprzestrzenia się w absurdalnym stopniu). O wiele łatwiej jest udowodnić, że zmienna członkowska lub parametr nie może mieć wartości zerowej, gdy jest oznaczony jako końcowy, ponieważ wiesz, że nie jest on ponownie przypisywany nigdzie indziej.

Moją sugestią jest, aby przyzwyczaić się do domyślnego stosowania finału dla członków i parametrów. To tylko kilka znaków, ale skłoni cię do poprawy stylu kodowania, jeśli nic więcej.

Finał dla metod lub klas to kolejna koncepcja, ponieważ nie pozwala na bardzo ważną formę ponownego użycia i tak naprawdę niewiele mówi czytelnikowi. Najlepszym zastosowaniem jest prawdopodobnie sposób, w jaki stworzyli String i inne wewnętrzne typy, abyś mógł polegać na konsekwentnym zachowaniu wszędzie - To zapobiegło wielu błędom (chociaż są chwile, gdybym LOVED wydłużył string ... och, możliwości)

Bill K.
źródło
0

Jak wspomniano w innym miejscu, „ostateczny” dla zmiennej lokalnej, aw nieco mniejszym stopniu zmiennej składowej, jest bardziej kwestią stylu.

„final” to stwierdzenie, że zmienna ma się nie zmieniać (tzn. zmienna się nie zmienia!). Kompilator może ci pomóc, składając skargę, jeśli naruszysz własne ograniczenia.

Podzielam pogląd, że Java byłby lepszym językiem, gdyby identyfikatory (przepraszam, po prostu nie mogę nazwać niczego niezmiennego „zmienną”) były domyślnie ostateczne i wymagałem, abyście wyraźnie powiedzieli, że są zmiennymi. Ale powiedziawszy to, ogólnie nie używam „końcowego” dla zmiennych lokalnych, które są inicjowane i nigdy nie są przypisywane; wydaje się po prostu zbyt głośno.

(Używam końcowego dla zmiennych składowych)

użytkownik13752845
źródło
-3

Członkowie zadeklarowani jako finałowi będą dostępni w całym programie, ponieważ w przeciwieństwie do nieoficjalnych, jeśli ci członkowie nie byli używani w programie, nadal nie byliby nimi objęci Garbage Collector, więc może to powodować problemy z wydajnością z powodu złego zarządzania pamięcią.

nams
źródło
2
Co to za nonsens? Czy masz jakieś źródło, dlaczego tak jest?
J. Doe
-4

final słowa kluczowego można używać na pięć sposobów w Javie.

  1. Klasa jest ostateczna
  2. Zmienna referencyjna jest ostateczna
  3. Zmienna lokalna jest ostateczna
  4. Metoda jest ostateczna

Klasa jest ostateczna: klasa jest ostateczna, co oznacza, że ​​nie możemy zostać przedłużeni lub dziedziczenie oznacza, że ​​dziedziczenie nie jest możliwe.

Podobnie - obiekt jest ostateczny: kiedyś nie zmodyfikowaliśmy wewnętrznego stanu obiektu, więc w takim przypadku możemy określić, że obiekt jest ostateczny. Przedmiot końcowy oznacza, że ​​zmienna również nie jest ostateczna.

Gdy zmienna referencyjna jest ostateczna, nie można jej ponownie przypisać do innego obiektu. Ale może zmienić zawartość obiektu, o ile jego pola nie są ostateczne

adityaraj
źródło
2
Przez „obiekt jest ostateczny” masz na myśli „obiekt jest niezmienny”.
gyorgyabraham
6
Masz rację, ale nie odpowiadasz na pytanie. OP nie zapytał, co to finalznaczy, ale czy użycie finalwpływa na wydajność.
Honza Zidek