wake-up-neo.com

Der Wert der statischen Variablen wurde auch nach dem Initialisieren der untergeordneten Klasse in Java nicht geändert

Wenn ich die statische Variable y mit Checks.y (Checks ist eine Unterklasse) aufrufe, wird der statische Block nicht ausgeführt und der Wert von y wird nicht aktualisiert.

class Par {
    static int y = 4;
}

class Checks extends Par {
    static {
        y = 5;
    }
}

public class Check {
    public static void main(String args[]) {
        System.out.println(Checks.y); // here printing 4
    }
}

Da statisch allen Unterklassen gemeinsam ist, soll der Wert aktualisiert werden.

Was könnte der Grund dafür sein?

32
rawat

Das Feld y wird nicht von der Klasse Checks deklariert.

Das Lesen statischer Felder löst keine Initialisierung der referenzierten Klasse (Checks) aus, es sei denn, diese Klasse ist die Klasse, in der das Feld deklariert ist (siehe JLS-Zitat unten). Selbst wenn auf y über Checks zugegriffen wird, wird in diesem Beispiel nur die Initialisierung von Par ausgelöst, da Par die Klasse ist, die y deklariert.

Die Klasse Checks wird also zur Laufzeit nicht verwendet.

Dies ist vielleicht ein Beispiel dafür, warum es falsch ist, über Unterklassen auf static-Mitglieder zuzugreifen, was zu einer Warnung während der Kompilierung führt.


Es gibt eine einfache Erklärung in der Spezifikation :

12.4.1. Wenn die Initialisierung auftritt

Eine Klasse oder ein Schnittstellentyp T lautet unmittelbar vor dem ersten Auftreten eines der folgende:

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

  • Eine von T deklarierte statische Methode wird aufgerufen.

  • Ein durch T deklariertes statisches Feld wird zugewiesen.

  • Ein von T deklariertes statisches Feld wird verwendet und das Feld ist keine Konstante Variable (§4.12.4).

  • T ist eine Klasse der obersten Ebene (§7.6) und eine Assert-Anweisung (§14.10) lexikalisch verschachtelt innerhalb von T (§8.1.3) wird ausgeführt . 
    ... 
    Ein Verweis auf ein statisches Feld (§8.3.1.1) bewirkt die Initialisierung nur der Klasse oder des Interfaces, die es tatsächlich deklariert, auch wenn auf den Namen einer Unterklasse, eines Subinterfaces oder einer Klasse, die ein Interface implementiert, verwiesen wird.

Der letzte Hinweis erläutert, warum Ihre Unterklasse nicht initialisiert wird.

29
ernest_k

Von JLS 12.4.1 :

Eine Klasse oder ein Schnittstellentyp T wird unmittelbar vor dem .__ initialisiert. Erstes Vorkommen eines der folgenden:

  • T ist eine Klasse und eine Instanz von T wird erstellt.
  • T ist eine Klasse und eine von T deklarierte statische Methode wird aufgerufen.
  • Ein durch T deklariertes statisches Feld wird zugewiesen.
  • Ein von T deklariertes statisches Feld wird verwendet und das Feld ist keine konstante Variable (§4.12.4).
  • T ist eine Klasse der obersten Ebene (§7.6), und eine innerhalb von T (§8.1.3) geschachtelte Aussage (§14.10) wird ausgeführt.

Da y nicht in Prüfungen deklariert wird, ist keines der oben genannten Kriterien erfüllt.

Eine andere Möglichkeit, dieses Verhalten zu veranschaulichen:

class par {
    static int y = 4;
    static {
        System.out.println("static constructor of par");
    }
}

class checks extends par {
    static int x = 6;
    static {
        System.out.println("checks static constructor");
        y = 5;
    }
}

public class check{
    public static void main(String args[]){
        System.out.println(checks.y);
        System.out.println(checks.x);
        System.out.println(checks.y);
    }
}

Ausgabe

static constructor of par
4
checks static constructor
6
5

Nach dem Aufruf von checks.x, der die zweite Regel erfüllt, wird der statische Konstruktor aufgerufen.

9
Diadistis

Hier:

System.out.println(checks.y); // Here printing 4

y bezieht sich auf ein Feld der par-Klasse. Dieser Feldzugriff führt zum Laden der par-Klasse (der übergeordneten Klasse) gemäß JLS (Schwerpunkt liegt bei mir):

12.4. Initialisierung von Klassen und Interfaces

....

12.4.1. Wenn die Initialisierung auftritt

Eine Klasse oder ein Schnittstellentyp T wird unmittelbar vor dem .__ initialisiert. Erstes Vorkommen eines der folgenden: T ist eine Klasse und eine Instanz von T wird erstellt. Eine statische Methode von T deklariert wird aufgerufen.

Ein durch T deklariertes statisches Feld wird zugewiesen.

Ein von T deklariertes statisches Feld wird verwendet und das Feld ist keine Konstante Variable (§4.12.4).

T ist eine Klasse der obersten Ebene (§7.6) und eine Assert-Anweisung (§14.10) lexikalisch innerhalb von T (§8.1.3) verschachtelt wird ausgeführt.

Die checks-Klasse wird jedoch nicht geladen, weil (Schwerpunkt liegt bei mir):

Ein Verweis auf ein statisches Feld (§8.3.1.1) bewirkt nur die Initialisierung von die Klasse oder Schnittstelle, die es tatsächlich deklariert, auch wenn kann durch den Namen einer Unterklasse, einer Subschnittstelle oder einer .__ referenziert werden. Klasse, die eine Schnittstelle implementiert.

4
davidxxx

Das liegt daran, dass der static-Block in der checks-Klasse nicht ausgeführt wird. Obwohl Sie die Klasse checks erwähnen, lädt die JVM sie überhaupt nicht.

Sie können dies bestätigen, indem Sie einen weiteren System.out.println innerhalb des statischen Blocks hinzufügen.

class checks extends par {

    static {
        System.out.println("Test");
        y = 5;
    }
}

Das Wort Test wird niemals gedruckt.


Lesen Sie den Abschnitt 12.4.1. Wenn die Initialisierung der Java-Sprachspezifikation auftritt, erfahren Sie mehr.

4

Der eine Aspekt, der bisher noch nicht erwähnt wurde, könnte für neue Java-Programmierer verwirrend sein: Die Tatsache, wie Sie Ihren Quellcode organisieren, ist bis zu einem gewissen Grad unerheblich! 

Sie haben Ihre zwei Klassen (der Name muss Parent und Child sein, um den Java-Namenskonventionen zu folgen) in einer Datei oder in einem Beispiel. Vermutlich gehen Sie also davon aus: Diese Dinge laufen zur Laufzeit automatisch zusammen. 

Zur Laufzeit gibt es jedoch einzelne Klassendateien pro Klasse. Und wie die anderen gesagt haben: nichts im Code verweist auf die Kindklasse. Diese Klasse wird also nicht geladen, daher erfolgt keine Zuordnung! 

1
GhostCat

Wie andere bereits erwähnen, wird der statische Block nicht ausgeführt, da die Klasse nicht als answer vor initialisiert wurde.

Beachten Sie, dass die Namen der Klassenkonventionen mit einem Großbuchstaben beginnen.

Ein klareres Beispiel zum Anzeigen der überschreibenden Klasse wird nicht verwendet:

class Par {
    static int y = 4;
    public static void main(String args[]) {
        System.out.println(Checks.y);    // Here printing 4
        System.out.println(new Checks().y);    // Here printing 5
    }
}

class Checks extends Par {
   static {
        y = 5;
    }
}
0
user7294900