Die Geschichte, dass die vom Java-Konstruktor aufgerufene Methode zur Variableninitialisierung nicht überschrieben werden sollte

Weil es normalerweise in einer bestimmten Android-Bibliothek verwendet wurde.

Problem

Sehen Sie plötzlich die Ausgabe der folgenden Implementierung?

public class Main {

    static class Foo {

        public Foo() {
            init();
        }

        protected void init() {
        }
    }

    static class Bar extends Foo {

        private int val1;

        private int val2 = 0;

        @Override
        protected void init() {
            val1 = 1;
            val2 = 2;
        }
    }

    public static void main(String... args) {
        Bar bar = new Bar();

        System.out.println(bar.val1);
        System.out.println(bar.val2);
    }
}

Das Ausgabeergebnis ist wie folgt.

1
0

Veranstaltung

Die Foo Klasse ist ein Konstruktor, der die init () Methode aufruft.

Die "Bar" -Klasse ist eine Unterklasse von "Foo", die die von "Foo" erstellte "init ()" - Methode zum Initialisieren von Mitgliedsvariablen überschreibt.

In "Bar.init ()" werden den Mitgliedsvariablen "val1" und "val2" "1" bzw. "2" zugewiesen.

Wenn Sie diese "Bar" -Klasse instanziieren und jeden Wert ausgeben, enthält "val1" "1", wie Sie sehen können, aber der Wert von "val2", dem "2" hätte zugewiesen werden sollen, ist "0" Es ist `.

Eine Falle, die bei der Initialisierung von Mitgliedsvariablen verborgen ist

Es gibt zwei Schlüssel, um dieses Phänomen zu enträtseln.

Eine ist die Definition von Mitgliedsvariablen und die andere ist der Initialisierungszeitpunkt von Variablen in Java.

Definition von Mitgliedsvariablen

In dem fraglichen Code sind die Mitgliedsvariablen der Klasse "Bar" wie folgt definiert:

Bar


private int val1;

private int val2 = 0;

In Bezug auf die Definition von Elementvariablen wird "0" für Grundelemente und "Null" für Klassen zugewiesen, wenn keine explizite Initialisierung durchgeführt wird. Selbst wenn es mit "0" initialisiert wird, ändert sich sein Verhalten abhängig davon, ob es explizit initialisiert wird oder nicht.

Initialisierungszeitpunkt für Java-Mitgliedsvariablen

In Java erfolgt die Initialisierung der Mitgliedsvariablen im Konstruktor. Die Frage ist, wann es im Konstruktor passiert.

Da ich denke, dass ich es normalerweise beiläufig schreibe, erfolgt die Initialisierung der Mitgliedsvariablen tatsächlich ** unmittelbar nach dem Aufruf des Konstruktors der übergeordneten Klasse **.

Mit anderen Worten

Foo


static class Foo {

    public Foo() {
← hier
        init();
    }
}

ist.

Ich glaube, Sie können nichts sehen, aber Javas Konstruktor erfordert ** den Aufruf des Konstruktors der übergeordneten Klasse am Anfang des Konstruktors **. Es gibt jedoch eine Bedingung, die weggelassen werden kann: ** Die übergeordnete Klasse definiert den Standardkonstruktor **. Der Standardkonstruktor ist ein Konstruktor, der keine Argumente akzeptiert. (Genau genommen ist es möglicherweise nicht der übergeordnete Konstruktor, aber ich werde es weglassen.)

Im obigen Code ist es also dasselbe wie ein super () Aufruf an der Pfeilposition.

Problembeschreibung

Auf der Grundlage des oben Gesagten werde ich die Falle erklären, auf die ich in dem fraglichen Code getreten bin.

Es war die Bar-Klasse, die in die Falle trat.

Bar


static class Bar extends Foo {

    private int val1;

    private int val2 = 0;

    @Override
    protected void init() {
        val1 = 1;
        val2 = 2;
    }
}

Wenn Sie diese Klasse schreiben, ohne sie wegzulassen, sieht sie wie folgt aus.

Bar


static class Bar extends Foo {

    private int val1;

    private int val2 = 0;

    public Bar() {
        super();
    }

    @Override
    protected void init() {
        val1 = 1;
        val2 = 2;
    }
}

Der Konstruktor "Bar" ruft "super ()" auf, dh den Konstruktor "Foo". Der Konstruktor "Foo" ruft "init ()" auf, was eine Überschreibung der "Bar" -Klasse darstellt, also einen Aufruf von "Bar.init ()".

Wenn Sie diesen Fluss grob erweitern

Bar() {

  super() {
    
    Bar.init() {
        Bar.val1 = 1;
        Bar.val2 = 2;
    }
  }
  
Initialisierung von Balkenelementvariablen() {
      Bar.val2 = 0;
  }
}

Es wird der Fluss sein.

Wird val1 nach super () nicht mit 0 initialisiert? Sie können denken, dass der Initialisierungsprozess selbst nach "super ()" nicht ausgeführt wird, wenn er zum Zeitpunkt der Definition nicht initialisiert wird.

Ergänzung

Übrigens, wenn Sie vor dem Zuweisen eines Werts auf "val2" von "Bar.init ()" verweisen, wird "0" zurückgegeben. Stellen Sie sich vor, dass allen Elementvariablen unabhängig von der Initialisierung der Elementdefinition "0" oder "Null" zugewiesen wird und der nach "super ()" angegebene Wert zugewiesen wird.

Die erste Implementierung, die ich veröffentlicht habe, ist der Mistcode. Warum also nicht verwenden, nachdem ich die Vererbung verstanden habe?

Referenz

(Es hätte offiziell geschrieben werden sollen, aber ich habe den Link vergessen. Wenn ich ihn finde, werde ich ihn einfügen ...)

Recommended Posts

Die Geschichte, dass die vom Java-Konstruktor aufgerufene Methode zur Variableninitialisierung nicht überschrieben werden sollte
Die Geschichte, dass das Servlet nicht in die Java-Webanwendung geladen werden konnte
[Java] Die Geschichte, dass das erwartete Array nicht von der String.split-Methode erhalten wurde.
Die Geschichte, dass das erzwungene Update nicht implementiert werden konnte
Die Geschichte, dass .java auch in Unity 2018 erstellt wurde
Der Fall, dass @Autowired in JUnit5 nicht verwendet werden konnte
[Gradle] Die Geschichte, dass die Klassendatei nicht in der JAR-Datei vorhanden war
[Swift] Variable Formen, die im Einführungsbuch nicht gelehrt wurden
Rufen Sie die Super-Methode in Java auf
Entspricht "Fehler, dass die Basisauthentifizierung nicht bestanden wird" im Testcode "Die Geschichte, die nicht gemacht werden konnte".
Ich habe versucht, die letzten 10 Fragen zu lösen, die nach der Registrierung bei AtCoder in Java gelöst werden sollten
Map keySet, Werte sollten nicht verwendet werden
Die Geschichte, die ich nach der Installation mehrerer Java unter Windows nicht erstellen konnte
Verwenden Sie Vorlagen für den Klassenpfad mit Apache Velocity
Die Geschichte, dass die vom Java-Konstruktor aufgerufene Methode zur Variableninitialisierung nicht überschrieben werden sollte
[Android] Lösung, wenn die Kamera unter Android 9 nicht gestartet werden kann
[Java] Wann var verwendet werden soll und wann nicht
Die Geschichte, dass das erzwungene Update nicht implementiert werden konnte
Was ist die Hauptmethode in Java?
Die Geschichte des Schreibens von Java in Emacs
Über den Fall, dass ("b" .. "aa") nicht in Ruby Range verwendet werden konnte
Die Geschichte, die ich nach der Installation mehrerer Java unter Windows nicht erstellen konnte
Ich wusste nicht, dass innere Klassen in der [Java] -Schnittstelle definiert werden können
[Java] Behandlung von Java Beans in der Methodenkette
Die Geschichte eines gewöhnlichen Othello in Java
Eine Geschichte über das JDK in der Java 11-Ära
[Java] KFunction von Method / Constructor in Java abrufen [Kotlin]
Die Geschichte, dass der Port im Spring-Boot-Beispielprogramm unbrauchbar wird
Grundlagen der Java-Fehlerbehandlung - Die Geschichte, die abfängt, wird nur im Vordergrund aufgegriffen
[Java] So lassen Sie den privaten Konstruktor in Lombok weg
Schreiben einer Klasse, die in Java bestellt werden kann Ein kleines Standard-Memo
[Java] Wann var verwendet werden soll und wann nicht
[Java] Der Abdeckungsbericht konnte nicht durch Kombinieren der Standardmethode der Cobertura + -Schnittstelle erstellt werden
"Die Sprachunterstützung für Java-Server ist in den letzten 3 Minuten fünfmal abgestürzt. Der Server wird nicht neu gestartet."
Rufen Sie Java im Zentrum von Technologiethemen und Elementartechnologien, die Java-Ingenieure 2017 verfolgen sollten.
Die Geschichte, dass der Anforderungsparameter aus der iPhone-Anwendung mit dem Servlet nicht erfolgreich abgerufen werden konnte
[Java 8] Sortiermethode in alphabetischer Reihenfolge und Reihenfolge der Zeichenkettenlänge, die für Codierungstests verwendet werden kann
[Java] Es scheint, dass Sie nicht "0 <hoge <10" in den bedingten Ausdruck der "if" -Anweisung schreiben können.