Wann werden statische Variablen initialisiert?

84

Ich frage mich, wann statische Variablen auf ihre Standardwerte initialisiert werden. Ist es richtig, dass beim Laden einer Klasse statische Variablen erstellt (zugewiesen) und dann statische Initialisierer und Initialisierungen in Deklarationen ausgeführt werden? Ab wann werden die Standardwerte angegeben? Dies führt zu dem Problem der Vorwärtsreferenz.

Bitte auch, wenn Sie dies anhand der Frage erklären können, warum statische Felder nicht rechtzeitig initialisiert werden. und besonders die Antwort von Kevin Brock auf derselben Seite. Ich kann den 3. Punkt nicht verstehen.

Ankit
quelle
2
Bitte bearbeiten Sie Ihre Frage so, dass sie das Zitat enthält, auf das Sie sich beziehen.
Oliver Charlesworth
1
Haben Sie die Java-Sprachspezifikation gelesen? Es ist absichtlich ein ziemlich lesbares Dokument. Wenn Sie das gelesen haben, verstehen Sie vielleicht, was los ist. Wenn nicht, können Sie mindestens eine spezifischere Frage stellen ...
Maarten Bodewes
Ich denke, diese Fragen und Antworten sind ein Dup von stackoverflow.com/questions/3499214 .
Stephen C

Antworten:

69

Von Siehe Java Static Variable Methoden :

  • Es ist eine Variable, die zur Klasse und nicht zum Objekt (Instanz) gehört.
  • Statische Variablen werden zu Beginn der Ausführung nur einmal initialisiert. Diese Variablen werden zuerst initialisiert, bevor Instanzvariablen initialisiert werden
  • Eine einzelne Kopie, die von allen Instanzen der Klasse gemeinsam genutzt werden soll
  • Auf eine statische Variable kann direkt über den Klassennamen zugegriffen werden und benötigt kein Objekt.

Instanz- und Klassenvariablen (statisch) werden automatisch auf Standardstandardwerte initialisiert, wenn Sie sie nicht absichtlich initialisieren. Obwohl lokale Variablen nicht automatisch initialisiert werden, können Sie kein Programm kompilieren, das weder eine lokale Variable initialisiert noch dieser lokalen Variablen einen Wert zuweist, bevor sie verwendet wird.

Was der Compiler tatsächlich tut, ist, intern eine einzelne Klasseninitialisierungsroutine zu erstellen, die alle statischen Variableninitialisierer und alle statischen Initialisierungscodeblöcke in der Reihenfolge kombiniert, in der sie in der Klassendeklaration erscheinen. Diese einzelne Initialisierungsprozedur wird nur einmal automatisch ausgeführt, wenn die Klasse zum ersten Mal geladen wird.

Bei inneren Klassen können sie keine statischen Felder haben

Eine innere Klasse ist eine verschachtelte Klasse, die nicht explizit oder implizit deklariert ist static.

...

Innere Klassen dürfen keine statischen Initialisierer (§8.7) oder Mitgliedsschnittstellen deklarieren ...

Innere Klassen dürfen keine statischen Elemente deklarieren, es sei denn, sie sind konstante Variablen ...

Siehe JLS 8.1.3 Innere Klassen und umschließende Instanzen

finalFelder in Java können getrennt von ihrem Deklarationsort initialisiert werden. Dies gilt jedoch nicht für static finalFelder. Siehe das folgende Beispiel.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

Dies ist , weil es nur eine ist Kopie der staticVariablen , die mit der Art, anstatt eines mit jeder Instanz des Typs zugeordnet ist, wie mit Instanzvariablen und wenn wir versuchen , initialisieren zvom Typ static finalinnerhalb des Konstruktor, es wird versuchen , das neu zu initialisieren static finalTyp - Feld zweil der Konstruktor bei jeder Instanziierung der Klasse ausgeführt wird, die bei statischen finalFeldern nicht auftreten darf .

Löwe
quelle
5
In case of static inner classes, they can not have static fieldsscheint ein Tippfehler. Innere Klassen sind nicht statisch.
Daniel Lubarov
Sie sollten jedoch anstelle von Obwohl
Suraj Jain
Wenn Sie eine JVM starten und zum ersten Mal eine Klasse laden (dies erfolgt durch den Klassenladeprogramm, wenn die Klasse zum ersten Mal in irgendeiner Weise referenziert wird), werden statische Blöcke oder Felder in die JVM 'geladen' und sind zugänglich.
nhoxbypass
1
Leider enthält diese Antwort einige sachliche Ungenauigkeiten darüber, wann die Statik initialisiert wird. Weitere Informationen finden Sie unter stackoverflow.com/a/3499322/139985 .
Stephen C
15

Sehen:

Insbesondere der letzte enthält detaillierte Initialisierungsschritte , die angeben, wann und in welcher Reihenfolge statische Variablen initialisiert werden (mit der Einschränkung, dass finalKlassenvariablen und Schnittstellenfelder, die Konstanten zur Kompilierungszeit sind, zuerst initialisiert werden).

Ich bin mir nicht sicher, was Ihre spezifische Frage zu Punkt 3 ist (vorausgesetzt, Sie meinen die verschachtelte?). Die detaillierte Sequenz besagt, dass dies eine rekursive Initialisierungsanforderung wäre, sodass die Initialisierung fortgesetzt wird.

Dave Newton
quelle
11

Statische Felder werden initialisiert, wenn die Klasse vom Klassenladeprogramm geladen wird. Zu diesem Zeitpunkt werden Standardwerte zugewiesen. Dies erfolgt in der Reihenfolge, in der sie im Quellcode erscheinen.

Dave
quelle
10

Die Reihenfolge der Initialisierung lautet:

  1. Statische Initialisierungsblöcke
  2. Instanzinitialisierungsblöcke
  3. Konstruktoren

Die Details des Prozesses werden im JVM- Spezifikationsdokument erläutert .

Óscar López
quelle
5

statische Variable

  • Es ist eine Variable, die zur Klasse und nicht zum Objekt (Instanz) gehört.
  • Statische Variablen werden zu Beginn der Ausführung nur einmal initialisiert (wenn der Classloader die Klasse zum ersten Mal lädt).
  • Diese Variablen werden zuerst initialisiert, bevor Instanzvariablen initialisiert werden
  • Eine einzelne Kopie, die von allen Instanzen der Klasse gemeinsam genutzt werden soll
  • Auf eine statische Variable kann direkt über den Klassennamen zugegriffen werden und benötigt kein Objekt
Aleroot
quelle
4

Beginnend mit dem Code aus der anderen Frage:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Ein Verweis auf diese Klasse startet die Initialisierung. Zunächst wird die Klasse als initialisiert markiert. Dann wird das erste statische Feld mit einer neuen Instanz von MyClass () initialisiert. Beachten Sie, dass myClass sofort einen Verweis auf eine leere MyClass-Instanz erhält . Das Leerzeichen ist vorhanden, aber alle Werte sind null. Der Konstruktor wird jetzt ausgeführt und gedrucktobj , was null ist.

Nun zurück zur Initialisierung der Klasse: Es objwird auf ein neues reales Objekt verwiesen, und wir sind fertig.

Wenn dies durch eine Anweisung wie: MyClass mc = new MyClass();ausgelöst wurde, wird erneut Platz für eine neue MyClass-Instanz zugewiesen (und die Referenz in platziert mc). Der Konstruktor wird erneut ausgeführt und erneut gedruckt obj, was jetzt nicht null ist.

Der eigentliche Trick dabei ist, dass bei der Verwendung von newwie in WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiisofort ein Verweis auf ein Bit des nullten Speichers gegeben wird. Die JVM initialisiert dann die Werte und führt den Konstruktor aus. Wenn Sie jedoch weii vorher irgendwie darauf verweisen - beispielsweise durch Verweisen auf einen anderen Thread oder durch Verweisen auf die Klasseninitialisierung -, sehen Sie sich eine mit Nullwerten gefüllte Klasseninstanz an.

RalphChapin
quelle
1
Eine Klasse wird erst nach Abschluss der Initialisierung als initialisiert markiert. Andernfalls wäre dies nicht sinnvoll. Das Markieren als initialisiert ist fast der letzte Schritt. Siehe JLS 12.4.2 .
Dave Newton
@ DaveNewton: Sobald etwas auf die Klasse verweist und mit der Initialisierung beginnt, behandeln alle weiteren Verweise die Klasse als initialisiert. Sie werden nicht versuchen, es zu initialisieren, und sie werden nicht darauf warten, dass es initialisiert wird. Daher können Felder, die von Beginn eines Programms an nicht null zu sein scheinen, tatsächlich eine Zeit lang null sein. Nicht zu verstehen, ist das, was die ganze Verwirrung verursacht. Ich denke, es ist am einfachsten zu sagen, dass eine nicht initialisierte Klasse bei der ersten Referenz als initialisiert "markiert" wird, alle anderen Referenzen behandeln sie als initialisiert, und deshalb geschieht dies.
RalphChapin
Eine Korrektur des vorherigen Kommentars: Wie in Dave Newtons JLS 12.4.2 beschrieben, wird die Klasse beim Initialisieren gesperrt . Andere Themen werden warten , bis die Klasse initialisiert werden. Dies hat jedoch keinen Einfluss auf diesen Fall, der alle in einem Thread abläuft.
RalphChapin
4

Die statische Variable kann auf die folgenden drei Arten initialisiert werden: Wählen Sie eine beliebige aus

  1. Sie können es zum Zeitpunkt der Deklaration initialisieren
  2. oder Sie können dies tun, indem Sie einen statischen Block erstellen, z.

    static {
            // whatever code is needed for initialization goes here
        }
  3. Es gibt eine Alternative zu statischen Blöcken - Sie können eine private statische Methode schreiben

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
Ajay Verma
quelle