Ich habe ein Programm wie dieses:
class Test {
final int x;
{
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
Wenn ich versuche, es auszuführen, erhalte ich einen Compilerfehler wie variable x might not have been initialized
folgt : Basierend auf Java-Standardwerten sollte ich die folgende Ausgabe richtig machen?
"Here x is 0".
Werden endgültige Variablen Standardwerte haben?
Wenn ich meinen Code so ändere,
class Test {
final int x;
{
printX();
x = 7;
printX();
}
Test() {
System.out.println("const called");
}
void printX() {
System.out.println("Here x is " + x);
}
public static void main(String[] args) {
Test t = new Test();
}
}
Ich erhalte folgende Ausgabe:
Here x is 0
Here x is 7
const called
Kann jemand bitte dieses Verhalten erklären ..
JLS ist zu sagen , dass Sie müssen den Standardwert leer letzte Instanz - Variable in Konstruktor zuweisen (oder in Initialisierungsbaustein das ist ziemlich gleich). Deshalb erhalten Sie im ersten Fall den Fehler. Es heißt jedoch nicht, dass Sie zuvor im Konstruktor nicht darauf zugreifen können. Sieht ein bisschen komisch aus, aber Sie können vor der Zuweisung darauf zugreifen und den Standardwert für int - 0 sehen.
UPD. Wie von @ I4mpi erwähnt, definiert JLS die Regel, dass jeder Wert vor jedem Zugriff definitiv zugewiesen werden sollte:
Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.
Es gibt jedoch auch eine interessante Regel in Bezug auf Konstruktoren und Felder:
If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.
Im zweiten Fall wird der Wert
x
also definitiv am Anfang des Konstruktors zugewiesen , da er die Zuweisung am Ende enthält.quelle
final
Feld lesen kann oder nicht , und wenn dieser Code sowohl vor als auch nach dem Schreiben des Feldes ausgeführt werden kann, hätte ein Compiler im allgemeinen Fall keine Möglichkeit dazu zu wissen, ob es jemals das Feld lesen würde, bevor es geschrieben wurde.Wenn Sie nicht initialisieren
x
, wird ein Fehler beim Kompilieren angezeigt, da dieserx
nie initialisiert wird.x
Als endgültig deklarieren bedeutet, dass es nur im Konstruktor oder im Initialisiererblock initialisiert werden kann (da dieser Block vom Compiler in jeden Konstruktor kopiert wird).Der Grund, warum Sie
0
vor der Initialisierung der Variablen ausgedruckt werden, ist auf das im Handbuch definierte Verhalten zurückzuführen (siehe Abschnitt "Standardwerte"):Standardwerte
Data Type Default Value (for fields) -------------------------------------- byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char '\u0000' String (or any object) null boolean false
quelle
Der erste Fehler ist, dass der Compiler sich beschwert, dass Sie ein letztes Feld haben, aber keinen Code, um es zu initialisieren - einfach genug.
Im zweiten Beispiel haben Sie Code, um ihm einen Wert zuzuweisen, aber die Reihenfolge der Ausführung bedeutet, dass Sie das Feld sowohl vor als auch nach dem Zuweisen referenzieren.
Der vorab zugewiesene Wert eines Feldes ist der Standardwert.
quelle
Alle nicht endgültigen Felder einer Klasse werden auf einen Standardwert initialisiert (
0
für nummerische Datentypen,false
für boolesche undnull
für Referenztypen, manchmal auch als komplexe Objekte bezeichnet). Diese Felder werden initialisiert, bevor ein Konstruktor (oder ein Instanzinitialisierungsblock) ausgeführt wird, unabhängig davon, ob die Felder vor oder nach dem Konstruktor deklariert wurden.Endgültige Felder einer Klasse haben keinen Standardwert und müssen nur einmal explizit initialisiert werden, bevor ein Klassenkonstruktor seinen Job beendet hat.
Lokale Variablen innerhalb eines Ausführungsblocks (z. B. einer Methode) haben keinen Standardwert. Diese Felder müssen vor ihrer ersten Verwendung explizit initialisiert werden, und es spielt keine Rolle, ob die lokale Variable als endgültig markiert ist oder nicht.
quelle
Lassen Sie es mich in den einfachsten Worten sagen, die ich kann.
final
Variablen müssen initialisiert werden, dies ist in der Sprachspezifikation vorgeschrieben. Bitte beachten Sie jedoch, dass eine Initialisierung zum Zeitpunkt der Deklaration nicht erforderlich ist.Dies muss initialisiert werden, bevor das Objekt initialisiert wird.
Wir können Initialisierungsblöcke verwenden, um die endgültigen Variablen zu initialisieren. Es gibt zwei Arten von Initialisierungsblöcken:
static
undnon-static
Der von Ihnen verwendete Block ist ein nicht statischer Initialisierungsblock. Wenn Sie also ein Objekt erstellen, ruft Runtime den Konstruktor auf, der wiederum den Konstruktor der übergeordneten Klasse aufruft.
Danach werden alle Initialisierer aufgerufen (in Ihrem Fall der nicht statische Initialisierer).
In Ihrer Frage, Fall 1 : Auch nach Abschluss des Initialisierungsblocks bleibt die endgültige Variable nicht initialisiert, was ein Fehler-Compiler erkennt.
In Fall 2 : Der Initialisierer initialisiert die endgültige Variable, daher weiß der Compiler, dass das Finale bereits initialisiert ist, bevor das Objekt initialisiert wird. Daher wird es sich nicht beschweren.
Nun ist die Frage, warum
x
nimmt eine Null. Der Grund hierfür ist, dass der Compiler bereits weiß, dass kein Fehler vorliegt. Beim Aufrufen der init-Methode werden alle Finals auf die Standardeinstellungen initialisiert und ein Flag gesetzt, das sie bei der tatsächlichen Zuweisungsanweisung ähnlich wie ändern könnenx=7
. Siehe den Init-Aufruf unten:quelle
Soweit mir bekannt ist, initialisiert der Compiler Klassenvariablen immer mit Standardwerten (sogar Endvariablen). Wenn Sie beispielsweise ein int für sich selbst initialisieren, wird das int auf den Standardwert 0 gesetzt. Siehe unten:
class Test { final int x; { printX(); x = this.x; printX(); } Test() { System.out.println("const called"); } void printX() { System.out.println("Here x is " + x); } public static void main(String[] args) { Test t = new Test(); } }
Das Obige würde Folgendes drucken:
Here x is 0 Here x is 0 const called
quelle
Nein. Diese Ausgabe wird nicht angezeigt, da in erster Linie ein Fehler bei der Kompilierung auftritt. Endgültige Variablen erhalten zwar einen Standardwert, aber für die Java-Sprachspezifikation (JLS) müssen Sie sie bis zum Ende des Konstruktors initialisieren (LE: Ich füge hier Initialisierungsblöcke ein). Andernfalls wird ein Fehler bei der Kompilierung angezeigt verhindert, dass Ihr Code kompiliert und ausgeführt wird.
In Ihrem zweiten Beispiel wird die Anforderung berücksichtigt. Deshalb wird (1) Ihr Code kompiliert und (2) Sie erhalten das erwartete Verhalten.
Versuchen Sie in Zukunft, sich mit dem JLS vertraut zu machen. Es gibt keine bessere Informationsquelle über die Java-Sprache.
quelle