Wie funktioniert das Schlüsselwort "final" in Java? (Ich kann immer noch ein Objekt ändern.)

480

In Java verwenden wir finalSchlüsselwörter mit Variablen, um anzugeben, dass deren Werte nicht geändert werden sollen. Aber ich sehe, dass Sie den Wert im Konstruktor / in den Methoden der Klasse ändern können. Wenn die Variable staticdann wieder ist, handelt es sich um einen Kompilierungsfehler.

Hier ist der Code:

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);
  }
}

Der obige Code funktioniert einwandfrei und ohne Fehler.

Ändern Sie nun die Variable wie folgt static:

private static final List foo;

Jetzt ist es ein Kompilierungsfehler. Wie funktioniert das finalwirklich?

GS
quelle
da foo nicht sichtbar ist - wie könnte es kompiliert werden?
Björn Hallström
5
@therealprashant das ist nicht wahr. Private statische Variablen sind gültig und können über statische Methoden in der Klasse, in der sie definiert sind, aufgerufen werden. Eine statische Variable bedeutet, dass die Variable einmal vorhanden ist und nicht an eine Instanz einer Klasse gebunden ist.
mbdavis
3
@mbdavis Oh ja! Danke. Trotzdem werde ich den Kommentar nicht löschen, um Leuten zu helfen, die wie ich denken, und dann wird Ihr Kommentar sie dazu bringen, in die richtige Richtung zu denken.
Therealprashant
@therealprashant okay, keine Sorge!
mbdavis
In Verbindung stehender Beitrag - Was entspricht Javas Finale in C #?
RBT

Antworten:

518

Sie dürfen immer eine Variable initialisierenfinal . Der Compiler stellt sicher, dass Sie dies nur einmal tun können.

Beachten Sie, dass das Aufrufen von Methoden für ein in einer finalVariablen gespeichertes Objekt nichts mit der Semantik von zu tun hat final. Mit anderen Worten: Es finalgeht nur um die Referenz selbst und nicht um den Inhalt des referenzierten Objekts.

Java hat kein Konzept der Objektveränderlichkeit; Dies wird durch sorgfältiges Entwerfen des Objekts erreicht und ist alles andere als trivial.

Marko Topolnik
quelle
12
versuche t.foo = new ArrayList () zu tun; in der Hauptmethode und Sie erhalten einen Kompilierungsfehler ... die Referenz foo ist nur an ein letztes Objekt von ArrayList gebunden ... es kann nicht auf eine andere ArrayList
verweisen
50
Hmmm. Es geht nur um Referenz, nicht um Wert. Vielen Dank!
GS
2
Ich habe eine Frage. Jemand, den ich kenne, der behauptet, "final" habe die Variable auch auf dem Stapel gespeichert. Ist das richtig? Ich habe überall gesucht und keine Referenz gefunden, die diese Behauptung bestätigen oder ablehnen könnte. Ich habe sowohl in der Java- als auch in der Android-Dokumentation gesucht. Auch nach "Java-Speichermodell" gesucht. Vielleicht funktioniert es unter C / C ++ so, aber ich glaube nicht, dass es unter Java so funktioniert. Hab ich recht?
Android-Entwickler
4
@androiddeveloper Nichts in Java kann die Stapel- / Heap-Platzierung explizit steuern. Insbesondere wird die vom HotSpot JIT-Compiler festgelegte Stapelplatzierung einer Escape-Analyse unterzogen , die weitaus aufwändiger ist als die Überprüfung, ob eine Variable vorhanden ist final. Veränderbare Objekte können ebenfalls stapelweise zugewiesen werden. finalFelder können zwar bei der Fluchtanalyse hilfreich sein, aber das ist ein ziemlich indirekter Weg. Beachten Sie auch, dass effektiv endgültige Variablen die gleiche Behandlung haben wie die finalim Quellcode markierten .
Marko Topolnik
5
finalist in der Klassendatei vorhanden und hat erhebliche semantische Konsequenzen für eine optimierte Laufzeit. Dies kann auch Kosten verursachen , da das JLS eine starke Garantie für die Konsistenz der finalFelder eines Objekts hat. Beispielsweise muss der ARM-Prozessor am Ende jedes Konstruktors einer Klasse mit finalFeldern einen expliziten Speicherbarrierebefehl verwenden. Auf anderen Prozessoren ist dies jedoch nicht erforderlich.
Marko Topolnik
574

Dies ist eine beliebte Interviewfrage . Mit diesen Fragen versucht der Interviewer herauszufinden, wie gut Sie das Verhalten von Objekten in Bezug auf Konstruktoren, Methoden, Klassenvariablen (statische Variablen) und Instanzvariablen verstehen.

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.
    }
}

Im obigen Fall haben wir einen Konstruktor für 'Test' definiert und ihm eine 'setFoo'-Methode gegeben.

Informationen zum Konstruktor: Der Konstruktor kann nur einmal pro Objekterstellung mithilfe des newSchlüsselworts aufgerufen werden . Sie können den Konstruktor nicht mehrmals aufrufen, da der Konstruktor nicht dafür ausgelegt ist.

Über die Methode: Eine Methode kann so oft aufgerufen werden, wie Sie möchten (auch nie), und der Compiler weiß es.

Szenario 1

private final List foo;  // 1

fooist ein Instanzvariable. Wenn wir ein TestKlassenobjekt erstellen foo, wird die Instanzvariable in das Objekt der TestKlasse kopiert . Wenn wir fooinnerhalb des Konstruktors zuweisen , weiß der Compiler, dass der Konstruktor nur einmal aufgerufen wird, sodass es kein Problem gibt, ihn innerhalb des Konstruktors zuzuweisen .

Wenn wir fooinnerhalb einer Methode zuweisen , weiß der Compiler, dass eine Methode mehrmals aufgerufen werden kann, was bedeutet, dass der Wert mehrmals geändert werden muss, was für eine finalVariable nicht zulässig ist. Der Compiler entscheidet also, dass der Konstruktor eine gute Wahl ist! Sie können einer endgültigen Variablen nur einmal einen Wert zuweisen.

Szenario 2

private static final List foo = new ArrayList();

fooist jetzt eine statische Variable. Wenn wir eine Instanz einer TestKlasse erstellen , foowird diese nicht in das Objekt kopiert, da sie foostatisch ist. Jetzt fooist nicht eine unabhängige Eigenschaft jedes Objekts. Dies ist eine Eigenschaft der TestKlasse. Aber fookann durch mehrere Objekte zu erkennen und , wenn jedes Objekt , das unter Verwendung des erstellte newSchlüsselwort , das letztlich das wird aufrufe TestKonstruktor , die den Wert zum Zeitpunkt der mehrere Objekterstellung ändert (Denken Sie daran , static foosich in jedem Objekt nicht kopiert, sondern zwischen mehreren Objekten gemeinsam .)

Szenario 3

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

Oben Modification-2ist von Ihrer Frage. Im obigen Fall ändern Sie nicht das erste referenzierte Objekt, sondern fügen Inhalte hinzu, foodie zulässig sind. Der Compiler beschwert sich, wenn Sie versuchen new ArrayList(), der fooReferenzvariablen a zuzuweisen .
Regel Wenn Sie eine finalVariable initialisiert haben , können Sie sie nicht so ändern, dass sie auf ein anderes Objekt verweist. (In diesem Fall ArrayList)

Finale Klassen können nicht unterklassiert werden
final Methoden außer Kraft gesetzt werden kann. (Dieses Verfahren ist in übergeordneter Klasse)
final Methoden überschreiben können. (Lesen Sie dies grammatikalisch. Diese Methode gehört zu einer Unterklasse.)

AmitG
quelle
1
Nur um das klar zu stellen. Wollen Sie in Szenario 2 sagen, dass foodies trotz der endgültigen Bezeichnung mehrmals festgelegt wird, wenn fooes in der Testklasse festgelegt ist und mehrere Testinstanzen erstellt werden?
Rawr
Habe nicht verstanden letzte Zeile für Szenario 2: Aber fookann .. mehrere Objekte sein). Ist das gemein, wenn ich zu einem Zeitpunkt mehr Objekte zu erstellen, dann , welchem Objekt initialisiert letzten Variable ist bei der Ausführung abhängt?
Saumya Suhagiya
1
Ich denke, eine hilfreiche Möglichkeit, über Szenario 3 nachzudenken, besteht darin, dass Sie finalder Speicheradresse zuweisen, auf die verwiesen wird, auf foodie sich eine ArrayList bezieht . Sie weisen nicht finaldie Speicheradresse zu , auf die das erste Element von foo(oder ein anderes Element in dieser Angelegenheit) verweist . Daher können Sie nicht ändern, fooaber Sie können ändern foo[0].
Pinkerton
@Rawr Derzeit würde Szenario 2 einen Fehler bei der Kompilierung verursachen, da foo = new ArrayList();- foosich auf die statische Variable bezieht, da wir uns in derselben Klasse befinden.
flow2k
Ich bin ein C ++ - Entwickler, der Java lernt. Ist es sicher, sich finaleine Variable als das constSchlüsselwort in C ++ vorzustellen?
Doug Barbieri
213

Das endgültige Schlüsselwort hat zahlreiche Verwendungsmöglichkeiten:

  • Eine letzte Klasse kann nicht unterklassiert werden.
  • Eine endgültige Methode kann nicht von Unterklassen überschrieben werden
  • Eine endgültige Variable kann nur einmal initialisiert werden

Andere Verwendung:

  • Wenn eine anonyme innere Klasse im Hauptteil einer Methode definiert ist, kann auf alle im Bereich dieser Methode als endgültig deklarierten Variablen innerhalb der inneren Klasse zugegriffen werden

Eine statische Klassenvariable ist ab dem Start der JVM vorhanden und sollte in der Klasse initialisiert werden. Die Fehlermeldung wird in diesem Fall nicht angezeigt.

czupe
quelle
24
Dies ist bei weitem meine Lieblingsantwort. Einfach und unkompliziert, das würde ich in Online-Dokumenten über Java erwarten.
RAnders00
Also können wir in statischen Variablen so oft initialisieren, wie wir wollen?
Jorge Saraiva
1
@jorgesaraiva ja, statische Variablen sind keine Konstanten.
Czupe
1
@jorgesaraiva Sie können Felder beliebig oft zuweisen (nicht initialisieren ) static(solange dies nicht der finalFall ist). In diesem Wiki finden Sie den Unterschied zwischen Zuweisung und Initialisierung .
56

Das finalSchlüsselwort kann je nach Verwendungszweck auf zwei verschiedene Arten interpretiert werden:

Werttypen: Für ints, doubles usw. wird sichergestellt, dass sich der Wert nicht ändern kann.

Referenztypen: Stellt bei Verweisen auf Objekte finalsicher, dass die Referenz niemals ändert, was bedeutet, dass sie immer auf dasselbe Objekt verweist. Es gibt keinerlei Garantie dafür, dass die Werte innerhalb des Objekts, auf die Bezug genommen wird, gleich bleiben.

Stellt daher final List<Whatever> foo;sicher, dass fooimmer auf dieselbe Liste Bezug genommen wird, der Inhalt dieser Liste sich jedoch im Laufe der Zeit ändern kann.

Smallhacker
quelle
23

Wenn Sie foostatisch machen , müssen Sie es im Klassenkonstruktor (oder in der Zeile, in der Sie es definieren) wie in den folgenden Beispielen initialisieren.

Klassenkonstruktor (keine Instanz):

private static final List foo;

static
{
   foo = new ArrayList();
}

In der Reihe:

private static final List foo = new ArrayList();

Das Problem hierbei ist nicht, wie der finalModifikator funktioniert, sondern wie der staticModifikator funktioniert.

Der finalModifikator erzwingt eine Initialisierung Ihrer Referenz bis zum Abschluss des Aufrufs Ihres Konstruktors (dh Sie müssen ihn im Konstruktor initialisieren).

Wenn Sie ein Attribut inline initialisieren, wird es initialisiert, bevor der Code ausgeführt wird, den Sie für den Konstruktor definiert haben, sodass Sie die folgenden Ergebnisse erhalten:

  • Wenn dies der Fall fooist static, foo = new ArrayList()wird es ausgeführt, bevor der static{}Konstruktor ausgeführt wird, den Sie für Ihre Klasse definiert haben
  • Wenn dies foonicht der Fall ist static, foo = new ArrayList()wird es ausgeführt, bevor Ihr Konstruktor ausgeführt wird

Wenn Sie ein Attribut nicht inline finalinitialisieren, erzwingt der Modifikator, dass Sie es initialisieren und dass Sie dies im Konstruktor tun müssen. Wenn Sie auch einen staticModifikator haben, ist der Konstruktor, in dem Sie das Attribut initialisieren müssen, der Initialisierungsblock der Klasse:static{} .

Der Fehler, den Sie in Ihrem Code erhalten, beruht auf der Tatsache, dass er static{}beim Laden der Klasse ausgeführt wird, bevor Sie ein Objekt dieser Klasse instanziieren. Daher haben Sie beim Erstellen fooder Klasse nicht initialisiert .

Stellen Sie sich den static{}Block als Konstruktor für ein Objekt vom Typ vor Class. Hier müssen Sie die Initialisierung Ihrer static finalKlassenattribute durchführen (falls dies nicht inline erfolgt).

Randnotiz:

Der finalModifikator stellt die Konstanz nur für primitive Typen und Referenzen sicher.

Wenn Sie ein finalObjekt deklarieren , erhalten Sie eine final Referenz auf dieses Objekt, aber das Objekt selbst ist nicht konstant.

Was Sie erzielen wirklich , wenn eine Deklaration finalAttribut ist , dass, sobald Sie ein Objekt für einen speziellen Zweck zu erklären (wie die , final Listdass Sie erklärt haben), das und nur das Objekt wird für diesen Zweck verwendet werden: Sie Änderungen nicht in der Lage List foozu eine andere List, aber Sie können Ihre ändern, Listindem Sie Elemente hinzufügen / entfernen (die von ListIhnen verwendeten Elemente sind dieselben, nur wenn der Inhalt geändert wird).

lucian.pantelimon
quelle
8

Dies ist eine sehr gute Interviewfrage. Manchmal werden Sie sogar gefragt, was der Unterschied zwischen einem endgültigen Objekt und einem unveränderlichen Objekt ist.

1) Wenn jemand ein endgültiges Objekt erwähnt, bedeutet dies, dass die Referenz nicht geändert werden kann, aber ihr Status (Instanzvariablen) kann geändert werden.

2) Ein unveränderliches Objekt ist ein Objekt, dessen Zustand nicht geändert werden kann, dessen Referenz jedoch geändert werden kann. Ex:

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

Die Referenzvariable x kann geändert werden, um auf eine andere Zeichenfolge zu verweisen, der Wert von "abc" kann jedoch nicht geändert werden.

3) Instanzvariablen (nicht statische Felder) werden beim Aufruf eines Konstruktors initialisiert. So können Sie Werte für Ihre Variablen in einem Konstruktor initialisieren.

4) "Aber ich sehe, dass Sie den Wert im Konstruktor / in den Methoden der Klasse ändern können". - Sie können es nicht innerhalb einer Methode ändern.

5) Eine statische Variable wird beim Laden der Klasse initialisiert. Sie können also nicht innerhalb eines Konstruktors initialisieren, dies muss bereits vorher erfolgen. Sie müssen also einer statischen Variablen während der Deklaration selbst Werte zuweisen .

user892871
quelle
7

Das finalSchlüsselwort in Java wird verwendet, um den Benutzer einzuschränken. Das Java- finalSchlüsselwort kann in vielen Zusammenhängen verwendet werden. Finale kann sein:

  1. Variable
  2. Methode
  3. Klasse

Das finalSchlüsselwort kann mit den Variablen angewendet werden. Eine finalVariable, die keinen Wert hat, wird als leere finalVariable oder nicht initialisierte finalVariable bezeichnet. Es kann nur im Konstruktor initialisiert werden. Es finalkann staticauch die leere Variable sein, die nur im staticBlock initialisiert wird .

Java letzte Variable:

Wenn Sie eine Variable als finalfestlegen, können Sie den Wert der finalVariablen nicht ändern (er ist konstant).

Beispiel einer finalVariablen

Es gibt eine endgültige Geschwindigkeitsbegrenzung für die Variable. Wir werden den Wert dieser Variablen ändern, sie kann jedoch nicht geändert werden, da die endgültige Variable, der einmal ein Wert zugewiesen wurde, niemals geändert werden kann.

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  

Java-Abschlussklasse:

Wenn Sie eine Klasse als finalerstellen, können Sie sie nicht erweitern .

Beispiel für die Abschlussklasse

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();  
      }  
  }  

Java endgültige Methode:

Wenn Sie eine Methode als endgültig festlegen, können Sie sie nicht überschreiben .

Beispiel für eine finalMethode (run () in Honda kann run () in Bike nicht überschreiben)

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();  
   }  
}  

geteilt von: http://www.javatpoint.com/final-keyword

Ali Ziaee
quelle
7

Erwähnenswert sind einige einfache Definitionen:

Klassen / Methoden

Sie können einige oder alle Klassenmethoden als deklarieren final, um anzuzeigen, dass die Methode nicht von Unterklassen überschrieben werden kann.

Variablen

Sobald eine finalVariable initialisiert wurde, enthält sie immer denselben Wert.

final Vermeiden Sie grundsätzlich das Überschreiben / Überschreiben durch irgendetwas (Unterklassen, Variable "Neuzuweisung"), je nach Fall.

ivanleoncz
quelle
1
Ich denke, die endgültige Definition von Variablen ist etwas kurz. "Wenn in Java das Schlüsselwort final mit einer Variablen primitiver Datentypen (int, float usw.) verwendet wird, kann der Wert der Variablen nicht geändert werden. Wenn final jedoch mit nicht primitiven Variablen verwendet wird (Beachten Sie, dass nicht primitive Variablen verwendet werden) sind immer Verweise auf Objekte in Java), können die Mitglieder des referenzierten Objekts geändert werden. final für nicht-primitive Variablen bedeutet nur, dass sie nicht geändert werden können, um auf ein anderes Objekt zu verweisen. " geeksforgeeks.org/g-fact-48
ceyun
Auch gültig, speziell für die Erwähnung als primitive und nicht-primitive Fälle. Tks.
ivanleoncz
4

finalist ein reserviertes Schlüsselwort in Java, um den Benutzer einzuschränken. Es kann auf Mitgliedsvariablen, Methoden, Klassen und lokale Variablen angewendet werden. Endvariablen werden staticin Java häufig mit dem Schlüsselwort deklariert und als Konstanten behandelt. Zum Beispiel:

public static final String hello = "Hello";

Wenn wir das finalSchlüsselwort mit einer Variablendeklaration verwenden, kann der in dieser Variablen gespeicherte Wert nicht geändert werden.

Zum Beispiel:

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

Hinweis : Eine als final deklarierte Klasse kann nicht erweitert oder vererbt werden (dh es kann keine Unterklasse der Superklasse geben). Es ist auch gut zu beachten, dass als final deklarierte Methoden nicht von Unterklassen überschrieben werden können.

Die Vorteile der Verwendung des endgültigen Schlüsselworts werden in diesem Thread behandelt .

Desta Haileselassie Hagos
quelle
2
the value stored inside that variable cannot be changed latterist teilweise wahr. Dies gilt nur für primitive Datentypen. finalWenn ein Objekt wie eine Arrayliste erstellt wird, kann sich sein Wert ändern, nicht jedoch die Referenz. Vielen Dank!
GS
3

Angenommen, Sie haben zwei Sparbüchsen, rot und weiß. Sie weisen diesen Sparbüchsen nur zwei Kinder zu und sie dürfen ihre Geldboxen nicht austauschen. Sie haben also rote oder weiße Sparbüchsen (endgültig). Sie können die Schachtel nicht ändern, aber Sie können Geld auf Ihre Schachtel legen. Niemand kümmert sich darum (Änderung-2).

Huseyin
quelle
2

Lesen Sie alle Antworten.

Es gibt einen anderen Benutzerfall, in dem finalSchlüsselwörter verwendet werden können, z. B. in einem Methodenargument:

public void showCaseFinalArgumentVariable(final int someFinalInt){

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

}

Kann für Variablen verwendet werden, die nicht geändert werden sollen.

Pritam Banerjee
quelle
1

Wenn Sie es statisch endgültig machen, sollte es in einem statischen Initialisierungsblock initialisiert werden

    private static final List foo;

    static {
        foo = new ArrayList();
    }

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

Das finalSchlüsselwort gibt an, dass eine Variable nur einmal initialisiert werden darf. In Ihrem Code führen Sie nur eine Initialisierung von final durch, damit die Bedingungen erfüllt sind. Diese Anweisung führt die Einzelinitialisierung von durch foo. Beachten Sie, dass final! = Unveränderlich bedeutet, dass sich die Referenz nicht ändern kann.

foo = new ArrayList();

Wenn Sie fooals deklarieren static final, muss die Variable beim Laden der Klasse initialisiert werden und kann sich beim Initialisieren nicht auf die Instanziierung (auch als Aufruf des Konstruktors bezeichnet) verlassenfoo da statische Felder ohne Instanz einer Klasse verfügbar sein müssen. Es gibt keine Garantie dafür, dass der Konstruktor vor Verwendung des statischen Felds aufgerufen wurde.

Wenn Sie Ihre Methode unter dem static finalSzenario ausführen , wird die TestKlasse vor dem Instanziieren geladen. Zu tdiesem Zeitpunkt gibt es keine Instanziierung der fooBedeutung, dass sie nicht initialisiert wurde. Daher foowird sie für alle Objekte auf den Standardwert gesetzt null. An dieser Stelle gehe ich davon aus, dass Ihr Code ein löst, NullPointerExceptionwenn Sie versuchen, ein Element zur Liste hinzuzufügen.

Kevin Bowersox
quelle
1

Zunächst ist hier die Stelle in Ihrem Code, an der Sie foo initialisieren (dh zum ersten Mal zuweisen):

foo = new ArrayList();

foo ist ein Objekt (mit Typ List), also ein Referenztyp , kein Werttyp (wie int). Als solches enthält es einen Verweis auf einen Speicherort (z. B. 0xA7D2A834), an dem Ihre Listenelemente gespeichert sind. Linien wie diese

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

Ändern Sie nicht den Wert von foo (der wiederum nur eine Referenz auf einen Speicherort ist). Stattdessen fügen sie einfach Elemente zu dem Speicherort hinzu, auf den verwiesen wird. Um das endgültige Schlüsselwort zu verletzen , müssten Sie erneut versuchen, foo wie folgt neu zuzuweisen:

foo = new ArrayList();

Das würde Ihnen einen Kompilierungsfehler geben.


Überlegen Sie sich nun, was passiert, wenn Sie das statische Schlüsselwort hinzufügen .

Wenn Sie NICHT über das statische Schlüsselwort verfügen, verfügt jedes Objekt, das die Klasse instanziiert, über eine eigene Kopie von foo. Daher weist der Konstruktor einer leeren, frischen Kopie der Variablen foo einen Wert zu, was vollkommen in Ordnung ist.

Wenn Sie jedoch das statische Schlüsselwort haben, ist nur ein foo im Speicher vorhanden, der der Klasse zugeordnet ist. Wenn Sie zwei oder mehr Objekte erstellen würden, würde der Konstruktor versuchen, dieses eine foo jedes Mal neu zuzuweisen, was das endgültige Schlüsselwort verletzt .

Niko Bellic
quelle
1
  1. Da die endgültige Variable nicht statisch ist, kann sie im Konstruktor initialisiert werden. Wenn Sie es jedoch statisch machen, kann es nicht vom Konstruktor initialisiert werden (da Konstruktoren nicht statisch sind).
  2. Es wird nicht erwartet, dass das Hinzufügen zur Liste endet, indem die Liste endgültig wird. finalbindet nur den Verweis auf ein bestimmtes Objekt. Es steht Ihnen frei, den 'Status' dieses Objekts zu ändern, nicht jedoch das Objekt selbst.
Ankit
quelle
1

Es folgen verschiedene Kontexte, in denen final verwendet wird.

Endvariablen Eine Endvariable kann nur einmal zugewiesen werden. Wenn die Variable eine Referenz ist, bedeutet dies, dass die Variable nicht erneut gebunden werden kann, um auf ein anderes Objekt zu verweisen.

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
   }
}

Der endgültigen Variablen kann später ein Wert zugewiesen werden (bei der Deklaration muss kein Wert zugewiesen werden), jedoch nur einmal.

Abschlussklassen Eine Abschlussklasse kann nicht erweitert (vererbt) werden.

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

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

Endgültige Methoden Eine endgültige Methode kann nicht von Unterklassen überschrieben werden.

//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
quelle
1

Ich dachte daran, hier eine aktualisierte und ausführliche Antwort zu schreiben.

final Schlüsselwort kann an mehreren Stellen verwendet werden.

  1. Klassen

Ein final classMittel , dass keine andere Klasse kann verlängern , dass die letzte Klasse. Wenn Java Run Time ( JRE ) weiß, dass eine Objektreferenz vom Typ einer endgültigen Klasse ist (z. B. F), weiß sie, dass der Wert dieser Referenz nur vom Typ F sein kann.

Ex:

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

Also , wenn es irgendeine Methode des Objekts führt, dass Verfahren nicht benötigt zur Laufzeit aufgelöst werden , um eine Verwendung von virtuellen Tabelle . dh Laufzeitpolymorphismus kann nicht angewendet werden. Die Laufzeit kümmert sich also nicht darum. Dies spart Verarbeitungszeit und verbessert die Leistung.

  1. Methoden

A final methodeiner Klasse bedeutet, dass eine untergeordnete Klasse, die diese Klasse erweitert, diese endgültige (n) Methode (n) nicht überschreiben kann . Das Laufzeitverhalten in diesem Szenario entspricht also auch dem vorherigen Verhalten, das ich für Klassen erwähnt habe.

  1. Felder, lokale Variablen, Methodenparameter

Wenn oben angegeben wurde final, bedeutet dies, dass der Wert bereits finalisiert ist und der Wert daher nicht geändert werden kann .

Ex:

Für Felder lokale Parameter

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

Für Methodenparameter

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

Dies bedeutet einfach, dass der Wert des finalReferenzwerts nicht geändert werden kann. dh nur eine Initialisierung ist zulässig. In diesem Szenario werden zur Laufzeit, da JRE weiß, dass Werte nicht geändert werden können, alle diese finalisierten Werte (der endgültigen Referenzen) in den L1-Cache geladen . Weil es nicht braucht , um wieder zu laden immer wieder aus dem Hauptspeicher . Andernfalls wird es in den L2-Cache geladen und von Zeit zu Zeit aus dem Hauptspeicher geladen. Es ist also auch eine Leistungsverbesserung.

In allen oben genannten drei Szenarien müssen finalwir uns keine Sorgen machen , wenn wir das Schlüsselwort nicht an Stellen angegeben haben, die wir verwenden können. Compiler-Optimierungen erledigen dies für uns. Es gibt noch viele andere Dinge, die Compiler-Optimierungen für uns tun. :) :)

Supun Wijerathne
quelle
0

Vor allem sind richtig. Wenn Sie nicht möchten, dass andere Unterklassen aus Ihrer Klasse erstellen, deklarieren Sie Ihre Klasse als endgültig. Dann wird es zur Blattebene Ihrer Klassenbaumhierarchie, die niemand weiter erweitern kann. Es ist eine gute Praxis, eine große Klassenhierarchie zu vermeiden.

Shehan Simen
quelle