Statische Initialisierer und statische Methoden in Java

8

Ruft das Aufrufen einer statischen Methode für eine Klasse in Java die Ausführung der statischen Initialisierungsblöcke auf?

Empirisch würde ich nein sagen. Ich habe so etwas:

%Vor%

Im Code, der die Klasse verwendet, mache ich etwas wie:

%Vor%

Das Problem ist, dass ich ein NullPointerException bekomme, weil die Karte ( allCountries ) nicht initialisiert ist. Wenn ich Haltepunkte im Block static aufstelle, kann ich sehen, dass die Karte korrekt ausgefüllt wird, aber es ist, als ob die statische Methode keine Kenntnis vom ausgeführten Initializer hat.

Kann jemand dieses Verhalten erklären?

Update : Ich habe dem Code mehr Details hinzugefügt. Es ist immer noch nicht 1: 1 (es gibt mehrere Maps und mehr Logik), aber ich habe explizit die Deklarationen / Referenzen von allCountries betrachtet und sie sind wie oben aufgelistet.

Sie können den vollständigen Initialisierungscode hier sehen.

Update # 2 : Ich habe versucht, den Code so weit wie möglich zu vereinfachen und habe ihn im Handumdrehen geschrieben. Der eigentliche Code hatte die statische Variablendeklaration nach dem Initialisierer. Dadurch wurde die Referenz zurückgesetzt, wie Jon in der Antwort unten erwähnte.

Ich habe den Code in meinem Beitrag geändert, um dies zu verdeutlichen. Daher ist es klarer für Leute, die die Frage finden. Sorry über die Verwirrung jeder. Ich habe nur versucht, jedem das Leben zu erleichtern :).

Danke für Ihre Antworten!

    
Alex Ciminian 13.03.2012, 22:09
quelle

4 Antworten

27
  

Ruft das Aufrufen einer statischen Methode für eine Klasse in Java die Ausführung der statischen Initialisierungsblöcke auf?

     

Empirisch würde ich nein sagen.

Sie haben Unrecht.

Aus dem JLS Abschnitt 8.7 :

  

Ein statischer Initialisierer, der in einer Klasse deklariert ist, wird ausgeführt, wenn die Klasse initialisiert wird (§ 12.4.2). Zusammen mit allen Feldinitialisierern für Klassenvariablen (§ 8.3.2) können statische Initialisierer verwendet werden, um die Klassenvariablen der Klasse zu initialisieren.

Abschnitt 12.4.1 von die JLS sagt:

  

Ein Klassen- oder Schnittstellentyp T wird unmittelbar vor dem ersten Auftreten eines der folgenden Ereignisse initialisiert:

     
  • T ist eine Klasse und eine Instanz von T wird erstellt.

  •   
  • T ist eine Klasse und eine statische Methode, die von T deklariert wird, wird aufgerufen.

  •   
  • Ein statisches Feld, das von T deklariert ist, wird zugewiesen.

  •   
  • Es wird ein statisches Feld verwendet, das von T deklariert wird, und das Feld ist keine konstante Variable (§4.12.4).

  •   
  • T ist eine Top-Level-Klasse (§7.6), und eine Assert-Anweisung (§14.10), die lexikalisch in T (§8.1.3) verschachtelt ist, wird ausgeführt.

  •   

Dies wird leicht angezeigt:

%Vor%

Ihr Problem liegt in einem Teil des Codes, den Sie uns nicht gezeigt haben. Meine Vermutung ist, dass Sie tatsächlich eine lokale Variable wie folgt deklarieren:

%Vor%

Das verbirgt die statische Variable und lässt die statische Variable leer. Wenn dies der Fall ist, ändern Sie es einfach in eine Zuweisung anstelle einer Deklaration:

%Vor%

BEARBEITEN: Ein Punkt, den es zu beachten gilt - obwohl Sie init() als allererste Zeile Ihres statischen Initialisierers haben, wenn Sie eigentlich vorher noch etwas anderes machen (möglicherweise in einer anderen Variablen) Initialisierer), die zu einer anderen Klasse aufrufen, und diese Klasse ruft zurück in Ihre Country -Klasse auf, dann wird dieser Code ausgeführt, während allCountries immer noch null ist.

EDIT: Okay, jetzt können wir deinen echten Code sehen, ich habe das Problem gefunden. Ihr Beitrag Code enthält Folgendes:

%Vor%

Aber Ihr echter Code hat folgendes:

%Vor%

Es gibt zwei wichtige Unterschiede:

  • Die Variablendeklaration tritt nach dem statischen Initialisierungsblock
  • auf
  • Die Variablendeklaration enthält eine explizite Zuweisung an null

Die Kombination dieser Methoden bringt Sie in Unordnung: Die Variableninitialisierer werden nicht alle vor dem statischen Initialisierer ausgeführt - die Initialisierung erfolgt in textueller Reihenfolge .

Sie füllen also die Sammlung ... und setzen dann den Verweis auf null.

Abschnitt 12.4.2 von Die JLS garantiert dies in Schritt 9 der Initialisierung:

  

Als nächstes führen Sie entweder die Initialisierer der Klassenvariablen und die statischen Initialisierer der Klasse oder die Feldinitialisierer der Schnittstelle in textlicher Reihenfolge aus, als wären sie ein einzelner Block.

Demonstrationscode:

%Vor%

Ausgabe:

%Vor%

Also ist die Lösung entweder , um die explizite Zuweisung an null loszuwerden, oder um die Deklarationen (und damit Initialisierer) vor den statischen Initialisierer zu verschieben, oder ( meine Vorliebe) beide.

    
Jon Skeet 13.03.2012, 22:16
quelle
2

Der statische Initialisierer wird aufgerufen, wenn die Klasse geladen wird, was normalerweise der Fall ist, wenn sie zuerst "erwähnt" wird. Wenn also eine statische Methode aufgerufen wird, wird der Initialisierer ausgelöst, wenn die Klasse zum ersten Mal referenziert wird.

Sind Sie sicher, dass die Nullzeiger-Ausnahme von allcountries.get() stammt und nicht von einer Null Country , die von get() zurückgegeben wird? Mit anderen Worten, sind Sie sicher, dass welches -Objekt null ist?

    
DNA 13.03.2012 22:15
quelle
2

Theoretisch sollte der statische Block ausgeführt werden, wenn der Classloader die Klasse lädt.

%Vor%

In Ihrem Code wird es initialisiert, wenn Sie das erste Mal die Klasse Country (wahrscheinlich die obige Zeile) erwähnen.

Ich habe das ausgeführt:

%Vor%

mit diesem:

%Vor%

und alles hat richtig funktioniert. Können Sie die Stack-Ablaufverfolgung des Fehlers buchen?

Ich würde sagen, dass das Problem in der Tatsache liegt, dass der statische Block vor dem eigentlichen Feld deklariert ist.

    
Jakub Zaverka 13.03.2012 22:16
quelle
0

Haben Sie allCountries = new HashMap(); in Ihrem statischen Initialisierungsblock? Der statische Initialisierungsblock wird nach Klasseninitialisierung .

    
Eugene Kuleshov 13.03.2012 22:17
quelle

Tags und Links