Heim  >  Artikel  >  Java  >  Lassen Sie das statische Schlüsselwort in Java sofort verstehen

Lassen Sie das statische Schlüsselwort in Java sofort verstehen

醉折花枝作酒筹
醉折花枝作酒筹nach vorne
2021-08-04 17:49:462134Durchsuche

Ich glaube, dass viele Schüler auf diese Art von Frage gestoßen sind. Sie haben die Informationen möglicherweise nachgeschlagen und sie dann vergessen. Wenn sie darauf stoßen, können sie sie immer noch nicht richtig beantworten. Als Nächstes werde ich Sie in vier Schritten dazu bringen, die Ausführungssequenz dieses Codes zu zerlegen und die Regeln zusammenzufassen.

Eine Eröffnungsfrage zur Untersuchung der Codeausführungssequenz:

public class Parent {
    static {
        System.out.println("Parent static initial block");
    }

    {
        System.out.println("Parent initial block");
    }

    public Parent() {
        System.out.println("Parent constructor block");

    }
}

public class Child extends Parent {
    static {
        System.out.println("Child static initial block");
    }

    {
        System.out.println("Child initial block");
    }
    
    private Hobby hobby = new Hobby();

    public Child() {
        System.out.println("Child constructor block");
    }
}

public class Hobby {
    static{
        System.out.println("Hobby static initial block");
    }

    public Hobby() {
        System.out.println("hobby constructor block");
    }
}

Was gibt der obige Code aus, wenn new Child() ausgeführt wird?

Ich glaube, dass viele Schüler auf ein solches Problem gestoßen sind. Möglicherweise haben sie die Informationen überprüft und sie dann vergessen. Wenn sie erneut darauf stoßen, können sie sie immer noch nicht richtig beantworten. Als Nächstes führt Sie der Klassensprecher durch vier Schritte, um die Ausführungssequenz dieses Codes zu zerlegen und die Regeln zusammenzufassen.

1. Was optimiert der Compiler?

Die folgenden beiden Codeteile vergleichen die Änderungen vor und nach der Kompilierung:

Child.java vor der Kompilierung

public class Child extends Parent {
    static {
        System.out.println("Child static initial block");
    }
    {
        System.out.println("Child initial block");
    }
    
    private Hobby hobby = new Hobby();
    
    public Child() {
        System.out.println("Child constructor block");
    }
}

Child.class nach der Kompilierung

public class Child extends Parent {
    private Hobby hobby;

    public Child() {
        System.out.println("Child initial block");
        this.hobby = new Hobby();
        System.out.println("Child constructor block");
    }

    static {
        System.out.println("Child static initial block");
    }
}

Durch den Vergleich können Sie erkennen, dass der Compiler den Initialisierungsblöcken und Werte zuweist Instanzfelder werden vor den Konstruktorcode verschoben und die Reihenfolge der zugehörigen Codes bleibt erhalten. Wenn mehrere Konstruktoren vorhanden sind, wird der Initialisierungscode tatsächlich kopiert und verschoben.

Auf dieser Grundlage können wir die erste Prioritätsreihenfolge ermitteln:

  • Initialisierungscode >

2.

Der Ladevorgang einer Klasse kann grob in drei Phasen unterteilt werden: Laden-> Link->

    Beim Instanziieren eines Objekts mit dem neuen Schlüsselwort
  • Lesen oder Festlegen statischer Felder eines Typs (außer „Konstante“))
  • Aufrufen einer statischen Methode eines Typs
  • Beim Aufrufen einer Klasse mit Reflexion
  • Wenn beim Initialisieren einer Klasse festgestellt wird, dass die übergeordnete Klasse nicht initialisiert wurde, wird zuerst die Initialisierung ihrer übergeordneten Klasse ausgelöst
  • Wenn die virtuelle Maschine gestartet wird, wird die Hauptklasse (die Klasse, die enthält) gestartet Die Methode main() wird zuerst initialisiert.
  • Wenn die MethodHandle-Instanz zum ersten Mal aufgerufen wird, wird die Klasse initialisiert, auf die die Methode zeigt, auf die MethodHandle verweist Wenn die Schnittstellenmethode in der Schnittstelle definiert ist, muss die Schnittstelle initialisiert werden, bevor die Elemente 2 und 3 durch statischen Code ausgelöst werden. Tatsächlich handelt es sich bei der Initialisierungsphase um den Ausführungsprozess Die vom Compiler automatisch generierte Klassenkonstruktormethode „static{}“ sammelt die Zuweisungsaktionen und statischen Anweisungsblöcke (static{}-Blöcke) aller statisch geänderten Klassenvariablen und behält die Reihenfolge bei, in der diese Codes angezeigt werden.
  • Gemäß Punkt 5 stellt die JVM sicher, dass die 583d030be372af71281df966e84181a5-Methode der Unterklasse beibehalten wird.

  • Zusammenfassend: Zugriff auf die Klasse Variablen oder statische Methoden lösen die Initialisierung der Klasse aus, und die Initialisierung der Klasse besteht darin, 583d030be372af71281df966e84181a5 auszuführen, d Zuerst wird die Unterklasseninitialisierung durchgeführt
  • Dies führt zur zweiten Prioritätsreihenfolge:

Statischer Code der übergeordneten Klasse > Statischer Code

3. Statischer Code wird nur einmal ausgeführt

Wir Jeder weiß, dass statischer Code (außer statischen Methoden) nur einmal ausgeführt wird.

Haben Sie jemals darüber nachgedacht, wie dieser Mechanismus gewährleistet ist?

Das übergeordnete Delegationsmodell in JDK8 :

    Anwendungsklassenlader → Erweiterungsklassenlader → Startklassenlader
  • In der normalen Entwicklung geschriebene Klassen werden standardmäßig von Anwendungsklassen geladen. Wenn der übergeordnete Klassenlader geladen wird, delegiert er an seine übergeordnete Klasse: den Erweiterungsklassenlader und Der Erweiterungsklassenlader delegiert an seine übergeordnete Klasse: Der Lader der übergeordneten Klasse kann die Ladeanforderung nur dann abschließen, wenn der Server den Ladevorgang selbst durchführt. Die Eltern-Kind-Beziehung zwischen den dreien wird nicht durch Vererbung erreicht, sondern durch den Kombinationsmodus. Die Implementierung dieses Prozesses ist ebenfalls sehr einfach:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
    // 首先检查该类是否被加载过
    // 如果加载过,直接返回该类
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 如果父类抛出ClassNotFoundException
            // 说明父类无法完成加载请求
        }

        if (c == null) {
            // 如果父类无法加载,转由子类加载
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

Kombiniert mit den Kommentaren, glaube ich es ist für jeden leicht zu verstehen.

Aus dem von den Eltern delegierten Code geht hervor, dass eine Klasse unter demselben Klassenlader nur einmal geladen werden kann, was die Initialisierung auf nur einmal beschränkt. Daher wird der statische Code in der Klasse (mit Ausnahme statischer Methoden) nur einmal während der Klasseninitialisierung ausgeführt. 7e51f00a783d7eb8f68358439dee7daf

Der vom Compiler automatisch generierte Klassenkonstruktor wurde zuvor eingeführt: b75c58c37951302f0f985c3101bcd2b3-Methode sammelt die Zuweisungsaktionen und statischen Anweisungsblöcke (static{}-Blöcke) aller statisch geänderten Klassenvariablen und behält die Reihenfolge des Erscheinens des Codes bei. Sie wird während der Klasseninitialisierung ausgeführt generiert auch eine < ;init>-Methode, sammelt die Zuweisungsaktion des Instanzfelds, den Code im Initialisierungsanweisungsblock ({}-Block) und den Konstruktor (Konstruktor) und behält die Reihenfolge des Erscheinens des Codes bei. Sie wird nach der neuen Anweisung ausgeführt

Wenn wir also eine neue Klasse erstellen und die JVM die Klasse nicht geladen hat, wird sie zuerst initialisiert und dann instanziiert.

An diesem Punkt kann die dritte Prioritätsregel herauskommen:

  • Statischer Code (statischer {}-Block, statische Feldzuweisungsanweisung) > Initialisierungscode ({}-Block, Instanzfeldzuweisungsanweisung)
5. Regelmäßige Praxis: Kombiniert die drei vorherigen Regeln und fasst die folgenden zwei zusammen:

1. Statischer Code (statischer {}-Block, statische Feldzuweisungsanweisung)

2. Statischer Code der übergeordneten Klasse

Gemäß der vorherigen Zusammenfassung werden der Initialisierungscode und der Konstruktorcode vom Compiler in 7e51f00a783d7eb8f68358439dee7daf gesammelt, und der statische Code wird in 583d030be372af71281df966e84181a5 gesammelt, sodass die oben genannten Regeln erneut zusammengeführt werden:

Übergeordnete Klasse

Üben wir es entsprechend der Frage am Anfang: 583d030be372af71281df966e84181a5 > 子类583d030be372af71281df966e84181a5 > 父类 7e51f00a783d7eb8f68358439dee7daf > 子类 7e51f00a783d7eb8f68358439dee7daf

Beim Ausführen von new Child() löst das Schlüsselwort new die Initialisierung der Child-Klasse aus. Wenn die JVM feststellt, dass sie über eine übergeordnete Klasse verfügt, initialisiert sie zunächst die übergeordnete Klasse und beginnt mit der Ausführung der Methode 583d030be372af71281df966e84181a5 und führt dann die Methode 583d030be372af71281df966e84181a5 aus (denken Sie daran, was in 583d030be372af71281df966e84181a5 gesammelt wird).

Dann beginnen wir mit der Instanziierung eines Objekts der Child-Klasse. Wir stellen fest, dass es eine übergeordnete Klasse hat Zuerst die übergeordnete Klasse und dann den Befehl 7e51f00a783d7eb8f68358439dee7daf der untergeordneten Klasse ausführen. (Erinnern Sie sich daran, was in 7e51f00a783d7eb8f68358439dee7daf gesammelt wird).

Ich glaube, nachdem Sie dies gelesen haben, haben Sie bereits die Antwort auf die Eröffnungsfrage. Sie können auch zuerst die Ausgabesequenz von Hand schreiben und dann den Code schreiben, um sie selbst zu überprüfen.

Fazit: Statik wird in der täglichen Entwicklung oft verwendet. Jedes Mal, wenn ich schreibe, gehen mir immer zwei Fragen durch den Kopf: Ist es in Ordnung, wenn ich es nicht verwende?

Wie Sie diesem Artikel entnehmen können, geht die Anwendung von Statik weit über Klassenvariablen hinaus und ist so einfach wie statische Methoden. Im klassischen Singleton-Muster werden Sie verschiedene Verwendungsmöglichkeiten von Statik sehen. Im nächsten Artikel erfahren Sie, wie Sie das Singleton-Muster auf ausgefallene Weise schreiben.

Das obige ist der detaillierte Inhalt vonLassen Sie das statische Schlüsselwort in Java sofort verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen