Dieser Artikel bietet Ihnen eine Einführung in den statischen Versand und den dynamischen Versand in Java (Codebeispiele). Ich hoffe, dass er für Sie hilfreich ist.
Bei der kürzlichen Überprüfung der JVM-Kenntnisse war das Verständnis von statischem Versand und dynamischem Versand etwas verwirrend. Daher habe ich versucht, den Code selbst zu schreiben und das Wissen in der Analyse zu festigen.
Es gibt den folgenden Codeabschnitt: Was gibt jeder Codeabschnitt aus?
package com.khlin.my.test; class Base { public static void foo() { System.out.println("Base.foo() invoked"); } public void bar(int c) { System.out.println("Base.bar(int) invoked"); } public void bar(Character c) { System.out.println("Base.bar(Character) invoked"); } public void baz(Object o) { System.out.println("Base.baz(Object) invoked"); } public void baz(Integer i) { System.out.println("Base.baz(Integer) invoked"); } } class Child extends Base { public static void foo() { System.out.println("Child.foo() invoked"); } public void bar(Character c) { System.out.println("Child.bar(Character) invoked"); } public void bar(char c) { System.out.println("Child.bar(char) invoked"); } } public class App { public static void main(String[] args) { Base child = new Child(); System.out.println("第1段输出:"); child.foo(); child.bar(new Character('C')); System.out.println("第2段输出:"); Object integer = new Integer(100); child.baz(integer); System.out.println("第3段输出:"); child.bar('C'); } }
Lassen Sie mich kurz den gesamten Prozess von der Codekompilierung bis zum Methodenaufruf vorstellen.
· Kompilieren
Sehen Sie sich zunächst die Ausgabe des ersten Absatzes an. Ruft child.foo() eine statische Methode der übergeordneten Klasse auf? oder die Kinderklasse?
Während der Kompilierungsphase erfolgt statischer Versand .
1 Base child = new Child();
Wenn wir ein Objekt erstellen, wie oben gezeigt, wird Base als statischer Typ der Variablen (Static Type) oder scheinbarer Typ (Apparent Type) bezeichnet ) wird das folgende Child als tatsächlicher Typ der Variablen (Actual Type) bezeichnet.
Alle Dispatch-Aktionen, die auf statischen Typen basieren, um die Ausführungsversion einer Methode zu finden, werden als statische Dispatch-Aktionen bezeichnet. Eine typische Anwendung des statischen Versands ist die Methodenüberladung, die während der Kompilierungsphase auftritt, sodass die als statisch versandte Aktion nicht tatsächlich von der virtuellen Maschine ausgeführt wird.
Der Empfänger (Reciever) der Methode und die Parameter der Methode werden gemeinsam als Methodenvariablen bezeichnet. Abhängig davon, auf wie vielen Arten von Variablen der Versand basiert, kann der Versand in Einzelversand und Mehrfachversand unterteilt werden. versenden.
Beim statischen Versand gibt es zwei Grundlagen für die Auswahl der Zielmethode Eine ist, ob der statische Typ Basis oder Kind ist, und die andere ist der Parametertyp der Methode. Daher ist der statische Versand ein Mehrfachversand.
Als nächstes werfen wir einen Blick auf die Anweisungen, die vom Code „Ausgabeabschnitt 1“ generiert werden. Die folgenden Ergebnisse werden durch die Anweisung javap -v App.class erhalten. Sie können die symbolischen Referenzen der beiden Anweisungen in den Zeilen 18 und 31 sehen, die mit der obigen Analyse übereinstimmen: Der statische Typ des untergeordneten Elements ist Base, also die Methode der Basisklasse wird ausgewählt; durch None Die Parameter- und Zeichentypen bestimmen jeweils, um welche Methodenversion es sich handelt.
Aber am Ende ist das Verhalten der beiden unterschiedlich. child.foo() ruft foo() vom statischen Typ Base auf, während child.bar(new Character(' C ')) soll die Methode des tatsächlichen Typs Child aufrufen.
Der Grund dafür ist, dass die beiden Anweisungen unterschiedlich sind: invokestatic und invokevirtual
Die Java Virtual Machine bietet 5 Methodenaufruf-Bytecode-Anweisungen:
invokestatic: Statische Methode aufrufen
invokespecial: Instanzkonstruktor
invokevirtual: Alle virtuellen Methoden aufrufen
invokeinterface: Schnittstellenmethode aufrufen, ein Objekt, das diese Schnittstelle implementiert wird zur Laufzeit bestimmt
invokedynamic: Analysieren Sie zunächst dynamisch die Methode, auf die der Aufrufpunktqualifizierer zur Laufzeit verweist, und führen Sie dann die Methode aus. Die vorherigen 4 Aufrufe Anweisungen und Versandlogik werden in der Java Virtual Machine verfestigt invokedynamic wird durch die vom Benutzer festgelegte Boot-Methode bestimmt.
Der konkrete Grund ist, dass sich verschiedene Anweisungen in der nächsten Phase (Analyse des Klassenladens) unterschiedlich verhalten. Lassen Sie uns dies zunächst beiseite legen und uns die Befehlsausgabe im zweiten Absatz ansehen.
Es ist ersichtlich, dass beim statischen Versand von die aufzurufende Methodenversion basierend auf dem statischen Typ der übergebenen Parameter bestimmt wird die Methode, Obwohl es eine baz(Integer)-Methode gibt, ist der statische Typ des übergebenen Integer-Parameters Object, daher wird baz(Object) aufgerufen.
Schauen wir uns die im dritten Absatz ausgegebenen Anweisungen an. Wir wissen, dass die Symbolreferenz immer noch eine Methode in der Basisklasse sein muss (obwohl es in der Child-Klasse eine bar(char c)-Methode mit denselben Parametern gibt Klasse), aber es gibt keine identischen Parameter in der Base-Methode (Char-Typ). Wird kein Fehler gemeldet? Welche Methode wird aufgerufen?
Es stellt sich heraus, dass der Compiler zwar die überladene Version einer Methode ermitteln kann, diese überladene Version jedoch in vielen Fällen nicht „die einzige“ ist und oft nur eine ermitteln kann Eine „angemessenere“ Version.
· Parsen des Klassenladens
Die Parsing-Phase ist der Prozess, in dem die virtuelle Maschine die Symbolreferenzen im Konstantenpool durch direkte ersetzt Referenzen.
Solange die Methode durch die Anweisungen „invokestatic“ und „invokespecial“ aufgerufen werden kann, kann die eindeutige Aufrufversion in der Analysephase bestimmt werden. Die vier Kategorien, die diese Bedingung erfüllen, sind statische Methoden, private Methoden, Instanzkonstruktoren. und übergeordnete Klassenmethoden Sie lösen die Symbolreferenz in eine direkte Referenz auf die Methode auf, wenn die Klasse geladen wird. Diese Methoden können als nicht virtuelle Methoden bezeichnet werden, und andere Methoden werden als nicht virtuelle Methoden bezeichnet (außer endgültige Methoden).
Die endgültige geänderte Methode wird mit der invokevirtual-Anweisung aufgerufen. Da sie jedoch nicht überschrieben werden kann und keine andere Version hat, handelt es sich auch um eine nicht virtuelle Methode.
Zurück zum ersten Absatz der Ausgabe: child.foo() ist eine invokestatic-Anweisung, daher wird sie während der Analysephase durch eine direkte Referenz ersetzt und die spezifische Klasse wird bestimmt, also der statische Typ Base .foo() heißt ).
Und child.bar(new Character('C')) ist invokevirtual. Zu diesem Zeitpunkt kann die Signatur der aufgerufenen Methode bestimmt werden, aber der tatsächliche Typ des Empfängers der Methode kann noch nicht bestimmt werden. Es wird durch dynamischen Versand bestimmt. Der dynamische Versand ist ein einzelner Versand, da es nur einen Clustereffekt gibt.
Im nächsten Schritt wird der tatsächliche Typ des Methodenempfängers ermittelt.
· Methodenaufruf zur Laufzeit
Bestimmen Sie die Methodenausführungsversion zur Laufzeit basierend auf dem tatsächlichen Typ Der Versandvorgang wird als dynamischer Versand bezeichnet.
Die endgültige Ausgabe ist:
Das obige ist der detaillierte Inhalt vonEinführung in den statischen Versand und den dynamischen Versand in Java (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!