Jak działa słowo kluczowe „końcowe” w Javie? (Nadal mogę modyfikować obiekt).

480

W Javie używamy finalsłowa kluczowego ze zmiennymi, aby określić, że jego wartości nie będą zmieniane. Ale widzę, że możesz zmienić wartość w konstruktorze / metodach klasy. Ponownie, jeśli zmienna jest, staticto jest to błąd kompilacji.

Oto kod:

import java.util.ArrayList;
import java.util.List;

class Test {
  private final List foo;

  public Test()
  {
      foo = new ArrayList();
      foo.add("foo"); // Modification-1
  }
  public static void main(String[] args) 
  {
      Test t = new Test();
      t.foo.add("bar"); // Modification-2
      System.out.println("print - " + t.foo);
  }
}

Powyższy kod działa poprawnie i nie zawiera błędów.

Teraz zmień zmienną jako static:

private static final List foo;

Teraz jest to błąd kompilacji. Jak to finalnaprawdę działa?

GS
źródło
skoro foo nie jest widoczne - jak można je skompilować?
Björn Hallström
5
@therealprashant, który nie jest prawdą. Prywatne zmienne statyczne są poprawne, są dostępne z metod statycznych w klasie, w której są zdefiniowane. Zmienna statyczna oznacza, że ​​zmienna istnieje raz i nie jest powiązana z instancją klasy.
mbdavis
3
@mbdavis O tak! Dziękuję Ci. Ale nadal nie usunę komentarza, aby pomóc osobom myślącym tak jak ja, a wtedy twój komentarz sprawi, że będą myśleć we właściwym kierunku.
tamalprashant
@therealprashant ok nie martw się!
mbdavis

Odpowiedzi:

518

Jesteś zawsze mogą zainicjować jest finalzmienną. Kompilator zapewnia, że ​​możesz to zrobić tylko raz.

Zauważ, że wywoływanie metod na obiekcie przechowywanym w finalzmiennej nie ma nic wspólnego z semantyką final. Innymi słowy: finaldotyczy tylko samego odwołania, a nie zawartości odnośnego obiektu.

Java nie ma pojęcia niezmienności obiektu; osiąga się to poprzez staranne zaprojektowanie obiektu i jest to przedsięwzięcie dalekie od trywialności.

Marko Topolnik
źródło
12
spróbuj zrobić t.foo = new ArrayList (); w głównej metodzie pojawi się błąd kompilacji ... referencyjne foo jest powiązane tylko z jednym końcowym obiektem ArrayList ... nie może wskazywać na żadną inną ArrayList
Code2Interface
50
hmmm. Chodzi o referencje, a nie o wartości. Dzięki!
GS
2
Mam pytanie. Ktoś, kogo znam, twierdził, że „ostateczny” również sprawia, że ​​zmienna jest przechowywana na stosie. Czy to jest poprawne? Szukałem wszędzie i nie mogłem znaleźć żadnego odniesienia, które mogłoby zaakceptować lub odrzucić to twierdzenie. Przeszukałem zarówno dokumentację Java, jak i Android. Szukano także „modelu pamięci Java”. Może działa w ten sposób na C / C ++, ale nie sądzę, że działa w ten sposób na Javie. Mam rację?
programista Androida
4
@androiddeveloper Nic w Javie nie może jawnie kontrolować umieszczania stosu / sterty. Mówiąc dokładniej, umieszczanie stosu zgodnie z decyzją kompilatora HotSpot JIT podlega analizie ucieczki , która jest o wiele bardziej zaangażowana niż sprawdzanie, czy zmienna jest final. Zmienne obiekty można również przypisywać do stosu. finalpola mogą jednak pomóc w analizie ucieczki, ale jest to dość pośrednia droga. Zauważ również, że efektywnie zmienne końcowe mają identyczne traktowanie jak zmienne zaznaczone finalw kodzie źródłowym.
Marko Topolnik
5
finaljest obecny w pliku klasy i ma znaczące konsekwencje semantyczne dla optymalizującego środowiska wykonawczego. Może to również powodować koszty, ponieważ JLS ma silną gwarancję spójności finalpól obiektu. Na przykład procesor ARM musi użyć wyraźnej instrukcji bariery pamięci na końcu każdego konstruktora klasy, która ma finalpola. W przypadku innych procesorów nie jest to jednak konieczne.
Marko Topolnik,
574

To jest ulubione pytanie do wywiadu . Na podstawie tych pytań ankieter próbuje dowiedzieć się, jak dobrze rozumiesz zachowanie obiektów w odniesieniu do konstruktorów, metod, zmiennych klas (zmiennych statycznych) i zmiennych instancji.

import java.util.ArrayList;
import java.util.List;

class Test {
    private final List foo;

    public Test() {
        foo = new ArrayList();
        foo.add("foo"); // Modification-1
    }

    public void setFoo(List foo) {
       //this.foo = foo; Results in compile time error.
    }
}

W powyższym przypadku zdefiniowaliśmy konstruktor dla „Testu” i nadaliśmy mu metodę „setFoo”.

O konstruktora: Konstruktor może być wywołany tylko jeden raz na tworzenie obiektów za pomocą newsłowa kluczowego. Nie można wywoływać konstruktora wiele razy, ponieważ konstruktor nie jest do tego przeznaczony.

Informacje o metodzie: Metodę można wywołać dowolną liczbę razy (nawet nigdy), a kompilator o tym wie.

Scenariusz 1

private final List foo;  // 1

foojest zmienną instancji . Kiedy tworzymy Testobiekt klasy, wówczas zmienna instancji foozostanie skopiowana do obiektu Testklasy. Jeśli przypiszemy foodo konstruktora, to kompilator wie, że konstruktor zostanie wywołany tylko raz, więc nie ma problemu z przypisaniem go do konstruktora.

Jeśli przypiszemy foowewnątrz metody, kompilator wie, że metodę można wywołać wiele razy, co oznacza, że ​​wartość będzie musiała zostać zmieniona wiele razy, co nie jest dozwolone dla finalzmiennej. Kompilator decyduje więc, że konstruktor jest dobrym wyborem! Możesz przypisać wartość do zmiennej końcowej tylko raz.

Scenariusz 2

private static final List foo = new ArrayList();

foojest teraz zmienną statyczną . Kiedy tworzymy instancję Testklasy, foonie zostanie ona skopiowana do obiektu, ponieważ foojest statyczna. Teraz foonie jest niezależną własnością każdego obiektu. Jest to właściwość Testklasy. Ale foomoże być widoczny dla wielu obiektów i jeśli każdy obiekt utworzony przy użyciu newsłowa kluczowego, które ostatecznie wywoła Testkonstruktor, który zmienia wartość w momencie tworzenia wielu obiektów (Pamiętaj, że static foonie jest kopiowany w każdym obiekcie, ale jest dzielony między wieloma obiektami .)

Scenariusz 3

t.foo.add("bar"); // Modification-2

Powyżej Modification-2jest z twojego pytania. W powyższym przypadku nie zmieniasz pierwszego obiektu, do którego istnieje odwołanie, ale dodajesz zawartość, fooktóra jest dozwolona. Kompilator narzeka jeśli spróbujesz przypisać new ArrayList()do foozmiennej referencyjnej.
Reguła Jeśli zainicjowałeś finalzmienną, nie możesz jej zmienić tak, aby odwoływała się do innego obiektu. (W tym przypadku ArrayList)

klas końcowych nie można podklasować. Metody
końcowe nie mogą zostać zastąpione. (Ta metoda jest w nadklasie) metody
końcowe mogą zostać zastąpione. (Przeczytaj to gramatycznie. Ta metoda należy do podklasy)

AmitG
źródło
1
Żeby było jasne. Czy w scenariuszu 2 mówisz, że foobyłby ustawiany wiele razy pomimo ostatecznego oznaczenia, gdyby fooustawiono go w klasie Test i utworzono wiele instancji Testu?
Rawr
Nie rozumiem ostatniej linii dla scenariusza 2: Ale foomoże być ... wiele obiektów.) Czy to znaczy, że jeśli tworzę wiele obiektów jednocześnie, to który obiekt inicjuje zmienną końcową zależy od wykonania?
Saumya Suhagiya
1
Myślę, że pomocnym sposobem myślenia o scenariuszu 3 jest przypisanie finaldo adresu pamięci, do fooktórego odwołuje się ArrayList. Nie przypisujesz finaladresu pamięci, do którego odwołuje się pierwszy element foo(lub dowolny element w tym zakresie). Dlatego nie możesz się zmienić, fooale możesz się zmienić foo[0].
Pinkerton,
@Rawr W obecnej postaci Scenariusz 2 spowodowałby błąd czasu kompilacji z powodu foo = new ArrayList();- fooodnosi się do zmiennej statycznej, ponieważ jesteśmy w tej samej klasie.
flow2k
Jestem programistą C ++ uczącym się języka Java. Czy można bezpiecznie traktować finalzmienną jako taką samą jak constsłowo kluczowe w C ++?
Doug Barbieri,
213

Ostateczne słowo kluczowe ma wiele sposobów użycia:

  • Ostateczna klasa nie może być podklasowana.
  • Ostatecznej metody nie można zastąpić podklasami
  • Ostatnią zmienną można zainicjować tylko raz

Inne zastosowanie:

  • Kiedy anonimowa klasa wewnętrzna jest zdefiniowana w ciele metody, wszystkie zmienne zadeklarowane jako ostateczne w zakresie tej metody są dostępne z klasy wewnętrznej

Zmienna klasy statycznej będzie istnieć od początku JVM i powinna zostać zainicjowana w klasie. Komunikat o błędzie nie pojawi się, jeśli to zrobisz.

czupe
źródło
24
To zdecydowanie moja ulubiona odpowiedź. Prosta i bezpośrednia, tego właśnie spodziewałbym się przeczytać w internetowych dokumentach dotyczących java.
RAnders00
Więc w zmiennych statycznych możemy inicjalizować tyle razy, ile chcemy?
jorge saraiva,
1
@jorgesaraiva tak, zmienne statyczne nie są stałymi.
czupe
1
@jorgesaraiva Możesz przypisywać (nie inicjować ) staticpola (o ile nie są final) tyle razy, ile chcesz. Zobacz tę wiki, aby zobaczyć różnicę między przypisaniem a inicjalizacją .
56

The final kluczowe można interpretować na dwa różne sposoby, w zależności od tego, na czym jest używane:

Typy wartości: dla ints,double s itp. Zapewni, że wartość nie może się zmienić,

Typy referencji: W przypadku referencji do obiektów finalzapewnia, że referencja nigdy się nie zmieni, co oznacza, że ​​zawsze będzie odnosić się do tego samego obiektu. Nie daje żadnej gwarancji, że wartości wewnątrz obiektu będą takie same.

Jako taki final List<Whatever> foo;zapewnia, że foozawsze odnosi się do tej samej listy, ale zawartość tej listy może się zmieniać z czasem.

Smallhacker
źródło
23

Jeśli zrobisz foostatyczny, musisz zainicjować go w konstruktorze klas (lub wstawić tam, gdzie go zdefiniujesz), tak jak w poniższych przykładach.

Konstruktor klas (nie instancja):

private static final List foo;

static
{
   foo = new ArrayList();
}

Inline:

private static final List foo = new ArrayList();

Problemem nie jest tutaj działanie finalmodyfikatora, ale raczej działanie staticmodyfikatora.

finalModyfikator wymusza inicjalizacja odniesienia do czasu połączenie do dopełnia konstruktorów (czyli trzeba go zainicjować w konstruktorze).

Po zainicjowaniu atrybutu in-line zostaje on zainicjowany przed uruchomieniem kodu zdefiniowanego dla konstruktora, dzięki czemu uzyskuje się następujące wyniki:

  • jeśli foojest static, foo = new ArrayList()zostanie wykonane przed wykonaniem static{}konstruktora zdefiniowanego dla twojej klasy
  • jeśli foonie static, foo = new ArrayList()zostanie wykonany przed uruchomieniem konstruktora

Jeśli nie zainicjujesz atrybutu w linii, finalmodyfikator wymusza jego inicjalizację i musisz to zrobić w konstruktorze. Jeśli masz również staticmodyfikator, konstruktorem, w którym będziesz musiał zainicjować atrybut, jest blok inicjalizacji klasy:static{} .

Błąd, który static{}pojawia się w kodzie, wynika z faktu, że jest uruchamiany podczas ładowania klasy, przed momentem utworzenia obiektu tej klasy. Dlatego nie zainicjujesz się foopodczas tworzenia klasy.

Pomyśl o static{}bloku jako konstruktorze obiektu typu Class. W tym miejscu musisz wykonać inicjalizację static finalatrybutów klasy (jeśli nie jest to wykonywane bezpośrednio).

Dygresja:

W finalmodyfikator zapewnieniu jak const-ności tylko dla typów pierwotnych i referencje.

Kiedy deklarujesz finalobiekt, otrzymujesz final odniesienie do tego obiektu, ale sam obiekt nie jest stały.

Deklarując finalatrybut, naprawdę osiągasz to, że po zadeklarowaniu obiektu do określonego celu (takiego jak ten final List, który zadeklarowałeś), tylko ten obiekt zostanie wykorzystany do tego celu: nie będziesz mógł zmienić List foona inny List, ale nadal możesz zmienić swoje List, dodając / usuwając elementy ( Listużywasz będzie taki sam, tylko ze zmienioną zawartością).

lucian.pantelimon
źródło
8

To bardzo dobre pytanie podczas rozmowy kwalifikacyjnej. Czasami mogą nawet zapytać, jaka jest różnica między ostatecznym obiektem a niezmiennym przedmiotem.

1) Gdy ktoś wspomina o obiekcie końcowym, oznacza to, że odwołania nie można zmienić, ale jego stan (zmienne instancji) można zmienić.

2) Niezmienny obiekt to taki, którego stanu nie można zmienić, ale można zmienić jego odniesienie. Dawny:

    String x = new String("abc"); 
    x = "BCG";

zmienną ref x można zmienić, aby wskazywała inny ciąg, ale nie można zmienić wartości „abc”.

3) Zmienne instancji (pola niestatyczne) są inicjowane, gdy wywoływany jest konstruktor. Możesz więc inicjalizować wartości dla zmiennych wewnątrz konstruktora.

4) „Ale widzę, że możesz zmienić wartość w konstruktorze / metodach klasy”. - Nie możesz tego zmienić wewnątrz metody.

5) Zmienna statyczna jest inicjowana podczas ładowania klasy. Więc nie możesz zainicjować wewnątrz konstruktora, musisz to zrobić jeszcze przed nim. Musisz więc przypisać wartości do zmiennej statycznej podczas samej deklaracji.

użytkownik892871
źródło
7

Słowo finalkluczowe w java służy do ograniczenia użytkownika. Słowo finalkluczowe java może być używane w wielu kontekstach. Finał może być:

  1. zmienna
  2. metoda
  3. klasa

Słowo finalkluczowe może być stosowane ze zmiennymi, finalzmienna, która nie ma wartości, nazywa się finalzmienną pustą lub finalzmienną niezainicjowaną . Można go zainicjować tylko w konstruktorze. finalZmienna pusta może być staticrównież inicjowana tylko w staticbloku.

Ostateczna zmienna Java:

Jeśli się dowolną zmienną jak final, to nie można zmienić wartości w finalzmiennej (To będzie stała).

Przykład finalzmiennej

Istnieje końcowa zmienna speedlimit, zmienimy wartość tej zmiennej, ale nie można jej zmienić, ponieważ zmiennej końcowej po przypisaniu wartości nigdy nie można zmienić.

class Bike9{  
    final int speedlimit=90;//final variable  
    void run(){  
        speedlimit=400;  // this will make error
    }  

    public static void main(String args[]){  
    Bike9 obj=new  Bike9();  
    obj.run();  
    }  
}//end of class  

Ostatnia klasa Java:

Jeśli utworzysz dowolną klasę jako final, nie możesz jej rozszerzyć .

Przykład końcowej klasy

final class Bike{}  

class Honda1 extends Bike{    //cannot inherit from final Bike,this will make error
  void run(){
      System.out.println("running safely with 100kmph");
   }  

  public static void main(String args[]){  
      Honda1 honda= new Honda();  
      honda.run();  
      }  
  }  

Ostateczna metoda Java:

Jeśli dowolna metoda zostanie uznana za ostateczną, nie można jej zastąpić .

Przykład finalmetody (run () w Hondzie nie może zastąpić run () w Bike)

class Bike{  
  final void run(){System.out.println("running");}  
}  

class Honda extends Bike{  
   void run(){System.out.println("running safely with 100kmph");}  

   public static void main(String args[]){  
   Honda honda= new Honda();  
   honda.run();  
   }  
}  

udostępnione z: http://www.javatpoint.com/final-keyword

Ali Ziaee
źródło
7

Warto wspomnieć o kilku prostych definicjach:

Klasy / metody

Można zadeklarować niektóre lub wszystkie metody klas jako final, aby wskazać, że metody nie można zastąpić podklasami.

Zmienne

Po zainicjowaniu finalzmienna zawsze zawiera tę samą wartość.

final w zasadzie unikaj nadpisywania / nadpisywania przez cokolwiek (podklasy, zmienna „zmiana przypisania”), w zależności od przypadku.

ivanleoncz
źródło
1
Myślę, że ostateczna definicja zmiennych jest nieco krótka; „W Javie, gdy słowo kluczowe final jest używane ze zmienną pierwotnych typów danych (int, float, .. itd.), Wartości zmiennej nie można zmienić, ale gdy final jest używane ze zmiennymi nieprymitywnymi (zwróć uwagę, że zmienne inne niż pierwotne są zawsze odniesieniami do obiektów w Javie), elementy odnośnego obiektu mogą zostać zmienione. w ostatecznym przypadku zmienne nieprymitywne oznaczają po prostu, że nie można ich zmienić w celu odniesienia do dowolnego innego obiektu ". geeksforgeeks.org/g-fact-48
ceyun
Obowiązuje również, szczególnie w przypadku wzmianki o przypadkach pierwotnych i nieprymitywnych. Tks.
ivanleoncz
4

finaljest zastrzeżonym słowem kluczowym w Javie w celu ograniczenia użytkownika i może być stosowane do zmiennych składowych, metod, klas i zmiennych lokalnych. Ostateczne zmienne są często deklarowane za pomocą staticsłowa kluczowego w Javie i są traktowane jako stałe. Na przykład:

public static final String hello = "Hello";

Kiedy używamy finalsłowa kluczowego z deklaracją zmiennej, wartości przechowywanej w tej zmiennej nie można później zmienić.

Na przykład:

public class ClassDemo {
  private final int var1 = 3;
  public ClassDemo() {
    ...
  }
}

Uwaga : Klasa zadeklarowana jako końcowa nie może być rozszerzana ani dziedziczona (tzn. Nie może istnieć podklasa superklasy). Warto również zauważyć, że metody zadeklarowane jako końcowe nie mogą zostać zastąpione przez podklasy.

Korzyści ze stosowania końcowego słowa kluczowego są omówione w tym wątku .

Desta Haileselassie Hagos
źródło
2
the value stored inside that variable cannot be changed latterjest częściowo prawdziwe. Dotyczy to tylko pierwotnych typów danych. W przypadku dowolnego obiektu utworzonego final, podobnie jak arraylist, jego wartość może ulec zmianie, ale nie odwołanie. Dziękuję Ci!
GS
3

Załóżmy, że masz dwie skarbonki, czerwoną i białą. Skarbonki przypisujesz tylko dwóm dzieciom i nie wolno im zamieniać swoich pudełek. Masz więc czerwone lub białe skarbonki (końcowe), więc nie możesz modyfikować pudełka, ale możesz włożyć pieniądze na swoje pudełko.

huseyin
źródło
2

Przeczytaj wszystkie odpowiedzi.

Istnieje inny przypadek użytkownika, w którym finalmożna użyć słowa kluczowego, tj. W argumencie metody:

public void showCaseFinalArgumentVariable(final int someFinalInt){

   someFinalInt = 9; // won't compile as the argument is final

}

Może być użyty do zmiennej, której nie należy zmieniać.

Pritam Banerjee
źródło
1

Po ustawieniu go na statyczny jako ostateczny, należy go zainicjalizować w bloku inicjalizacji statycznej

    private static final List foo;

    static {
        foo = new ArrayList();
    }

    public Test()
    {
//      foo = new ArrayList();
        foo.add("foo"); // Modification-1
    }
Evgeniy Dorofeev
źródło
1

Słowo finalkluczowe wskazuje, że zmienną można zainicjować tylko raz. W kodzie wykonujesz tylko jedną inicjalizację finału, więc warunki są spełnione. Ta instrukcja wykonuje samotną inicjalizację foo. Zauważ, że final! = Niezmienny, oznacza to tylko, że odwołanie nie może się zmienić.

foo = new ArrayList();

Kiedy deklarujesz foojako static finalzmienna musi być inicjowane, gdy klasa jest załadowany i nie można polegać na konkretyzacji (aka zadzwonić do konstruktora), aby zainicjować foood pola statyczne muszą być dostępne bez instancji klasy. Nie ma gwarancji, że konstruktor zostanie wywołany przed użyciem pola statycznego.

Gdy wykonasz metodę w tym static finalscenariuszu, Testklasa jest ładowana przed utworzeniem instancji tw tym momencie, nie ma instancji, fooco oznacza, że ​​nie została zainicjowana, więc foojest ustawiona na wartość domyślną dla wszystkich obiektów, które są null. W tym momencie zakładam, że Twój kod rzuca, NullPointerExceptiongdy próbujesz dodać element do listy.

Kevin Bowersox
źródło
1

Po pierwsze, miejsce w twoim kodzie, w którym inicjujesz (tj. Przypisujesz po raz pierwszy) foo jest tutaj:

foo = new ArrayList();

foo jest obiektem (z typem List), więc jest typem referencyjnym , a nie typem wartości (jak int). Jako taki zawiera odniesienie do lokalizacji w pamięci (np. 0xA7D2A834), w której przechowywane są elementy listy. Takie linie

foo.add("foo"); // Modification-1

nie zmieniaj wartości foo (która znowu jest tylko odniesieniem do lokalizacji w pamięci). Zamiast tego po prostu dodają elementy do wskazanej lokalizacji pamięci. Aby naruszyć końcowe słowo kluczowe, musisz spróbować ponownie przypisać foo w następujący sposób:

foo = new ArrayList();

Że byłoby dać błąd kompilacji.


Teraz, mając to na uwadze, zastanów się, co się stanie, gdy dodasz słowo kluczowe static .

Jeśli NIE masz statycznego słowa kluczowego, każdy obiekt, który tworzy instancję klasy, ma własną kopię foo. Dlatego konstruktor przypisuje wartość do pustej, świeżej kopii zmiennej foo, co jest całkowicie w porządku.

Jednakże, gdy masz słowo kluczowe static, w pamięci istnieje tylko jedna foo powiązana z klasą. Jeśli chcesz utworzyć dwa lub więcej obiektów, konstruktor będzie próbował za każdym razem ponownie przypisać ten jeden foo, naruszając ostateczne słowo kluczowe.

Niko Bellic
źródło
1
  1. Ponieważ zmienna końcowa jest niestatyczna, można ją zainicjować w konstruktorze. Ale jeśli ustawisz go na statyczny, nie może zostać zainicjowany przez konstruktora (ponieważ konstruktory nie są statyczne).
  2. Dodanie do listy nie powinno się kończyć, czyniąc listę ostateczną. finalpo prostu wiąże odniesienie do określonego obiektu. Możesz zmienić „stan” tego obiektu, ale nie sam obiekt.
Ankit
źródło
1

Poniżej przedstawiono różne konteksty, w których używany jest finał.

Zmienne końcowe Ostatnią zmienną można przypisać tylko raz. Jeśli zmienna jest odwołaniem, oznacza to, że zmiennej nie można ponownie powiązać z odniesieniem do innego obiektu.

class Main {
   public static void main(String args[]){
      final int i = 20;
      i = 30; //Compiler Error:cannot assign a value to final variable i twice
   }
}

zmiennej końcowej można przypisać wartość później (nieobowiązkowo przypisać wartość, gdy zostanie zadeklarowana), ale tylko raz.

Klasy końcowe Klasy końcowej nie można przedłużać (dziedziczone)

final class Base { }
class Derived extends Base { } //Compiler Error:cannot inherit from final Base

public class Main {
   public static void main(String args[]) {
   }
}

Ostateczne metody Ostatecznej metody nie można zastąpić podklasami.

//Error in following program as we are trying to override a final method.
class Base {
  public final void show() {
       System.out.println("Base::show() called");
    }
}     
class Derived extends Base {
    public void show() {  //Compiler Error: show() in Derived cannot override
       System.out.println("Derived::show() called");
    }
}     
public class Main {
    public static void main(String[] args) {
        Base b = new Derived();;
        b.show();
    }
}
roottraveller
źródło
1

Myślałem o napisaniu tutaj zaktualizowanej i dogłębnej odpowiedzi.

final słowa kluczowego można użyć w kilku miejscach.

  1. zajęcia

A final classoznacza, że żaden inny klasa może przedłużyć ten ostateczny klasie. Gdy Java Run Time ( JRE ) wie, że odwołanie do obiektu jest w rodzaju klasy końcowej (powiedzmy F), wie, że wartość tego odwołania może być tylko typu F.

Dawny:

F myF;
myF = new F();    //ok
myF = someOther;  //someOther cannot be in type of a child class of F.
                  //because F cannot be extended.

Kiedy więc wykonuje jakąkolwiek metodę tego obiektu, tej metody nie trzeba rozwiązywać w czasie wykonywania przy użyciu wirtualnej tabeli . tzn. nie można zastosować polimorfizmu w czasie wykonywania. Czas wykonywania nie zawraca sobie tym głowy. Co oznacza, że ​​oszczędza czas przetwarzania, co poprawi wydajność.

  1. metody

A final methoddowolnej klasy oznacza, że ​​żadna klasa potomna rozszerzająca tę klasę nie może przesłonić ostatecznych metod. Zatem zachowanie w czasie wykonywania w tym scenariuszu jest również takie samo, jak poprzednie zachowanie, o którym wspominałem dla klas.

  1. pola, zmienne lokalne, parametry metody

Jeśli określono jakikolwiek z powyższych elementów jako final, oznacza to, że wartość jest już sfinalizowana, więc wartości nie można zmienić .

Dawny:

W przypadku pól parametry lokalne

final FinalClass fc = someFC; //need to assign straight away. otherwise compile error.
final FinalClass fc; //compile error, need assignment (initialization inside a constructor Ok, constructor can be called only once)
final FinalClass fc = new FinalClass(); //ok
fc = someOtherFC; //compile error
fc.someMethod(); //no problem
someOtherFC.someMethod(); //no problem

Dla parametrów metody

void someMethod(final String s){
    s = someOtherString; //compile error
}

Oznacza to po prostu, że wartości wartości finalodniesienia nie można zmienić. tzn. dozwolona jest tylko jedna inicjalizacja. W tym scenariuszu w czasie wykonywania, ponieważ środowisko JRE wie, że wartości nie można zmienić, ładuje wszystkie te ostateczne wartości (ostatecznych odniesień) do pamięci podręcznej L1 . Ponieważ nie trzeba go ponownie ładować z pamięci głównej . W przeciwnym razie ładuje się do pamięci podręcznej L2 i od czasu do czasu ładuje się z pamięci głównej. Jest to więc także poprawa wydajności.

Tak więc we wszystkich powyższych 3 scenariuszach, gdy nie określiliśmy finalsłowa kluczowego w miejscach, których możemy użyć, nie musimy się martwić, optymalizacje kompilatora zrobią to za nas. Istnieje również wiele innych rzeczy, które optymalizacje kompilatora robią dla nas. :)

Supun Wijerathne
źródło
0

Przede wszystkim są poprawne. Ponadto, jeśli nie chcesz, aby inni tworzyli podklasy z twojej klasy, zadeklaruj swoją klasę jako ostateczną. Następnie poziom liścia w hierarchii drzewa klasowego staje się tym, że nikt nie może go dalej rozszerzać. Dobrą praktyką jest unikanie ogromnej hierarchii klas.

Shehan Simen
źródło