Nun, um zu verstehen, wie statische und dynamische Bindung tatsächlich funktioniert? oder wie werden sie von Compiler und JVM identifiziert?
Nehmen wir das folgende Beispiel, in dem Mammal
eine übergeordnete Klasse mit einer Methode speak()
und einer erweiterten Human
Klasse Mammal
die speak()
Methode überschreibt und sie dann erneut überlädt speak(String language)
.
public class OverridingInternalExample {
private static class Mammal {
public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
}
private static class Human extends Mammal {
@Override
public void speak() { System.out.println("Hello"); }
// Valid overload of speak
public void speak(String language) {
if (language.equals("Hindi")) System.out.println("Namaste");
else System.out.println("Hello");
}
@Override
public String toString() { return "Human Class"; }
}
// Code below contains the output and bytecode of the method calls
public static void main(String[] args) {
Mammal anyMammal = new Mammal();
anyMammal.speak(); // Output - ohlllalalalalalaoaoaoa
// 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Mammal humanMammal = new Human();
humanMammal.speak(); // Output - Hello
// 23: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Human human = new Human();
human.speak(); // Output - Hello
// 36: invokevirtual #7 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V
human.speak("Hindi"); // Output - Namaste
// 42: invokevirtual #9 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
}
}
Wenn wir den obigen Code kompilieren und versuchen, den Bytecode mit zu betrachten javap -verbose OverridingInternalExample
, können wir sehen, dass der Compiler eine konstante Tabelle generiert, in der er jedem Methodenaufruf und Bytecode für das Programm, das ich extrahiert und in das Programm selbst aufgenommen habe, Ganzzahlcodes zuweist ( siehe die Kommentare unter jedem Methodenaufruf)
Mit Blick auf obigem Code können wir , dass die Bytecodes sehen humanMammal.speak()
, human.speak()
und human.speak("Hindi")
sind völlig anders ( invokevirtual #4
, invokevirtual #7
, invokevirtual #9
) , da der Compiler zwischen ihnen zu unterscheiden , ist in der Lage , basierend auf der Liste der Argumente und Klassenreferenz. Da all dies zur Kompilierungszeit statisch gelöst wird, wird das Überladen von Methoden als statischer Polymorphismus oder statische Bindung bezeichnet .
Aber Bytecode für anyMammal.speak()
und humanMammal.speak()
ist gleich ( invokevirtual #4
), da laut Compiler beide Methoden aufgerufen werdenMammal
Referenz .
Nun stellt sich die Frage, ob beide Methodenaufrufe denselben Bytecode haben. Woher weiß JVM dann, welche Methode aufgerufen werden soll?
Nun, die Antwort ist im Bytecode selbst versteckt und es handelt sich um einen invokevirtual
Befehlssatz. JVM verwendet dieinvokevirtual
Anweisung, um ein Java-Äquivalent der virtuellen C ++ - Methoden aufzurufen. Wenn wir in C ++ eine Methode in einer anderen Klasse überschreiben möchten, müssen wir sie als virtuell deklarieren. In Java sind jedoch alle Methoden standardmäßig virtuell, da wir jede Methode in der untergeordneten Klasse überschreiben können (außer privaten, endgültigen und statischen Methoden).
In Java enthält jede Referenzvariable zwei versteckte Zeiger
- Ein Zeiger auf eine Tabelle, die wiederum Methoden des Objekts enthält, und ein Zeiger auf das Class-Objekt. zB [speak (), speak (String) Klassenobjekt]
- Ein Zeiger auf den auf dem Heap zugewiesenen Speicher für die Daten dieses Objekts, z. B. Werte von Instanzvariablen.
Alle Objektreferenzen enthalten also indirekt eine Referenz auf eine Tabelle, die alle Methodenreferenzen dieses Objekts enthält. Java hat dieses Konzept von C ++ ausgeliehen und diese Tabelle wird als virtuelle Tabelle (vtable) bezeichnet.
Eine vtable ist eine Array-ähnliche Struktur, die virtuelle Methodennamen und deren Referenzen auf Array-Indizes enthält. JVM erstellt nur eine vtable pro Klasse, wenn die Klasse in den Speicher geladen wird.
Wenn JVM also auf einen invokevirtual
Befehlssatz stößt , überprüft es die vtable dieser Klasse auf die Methodenreferenz und ruft die spezifische Methode auf, die in unserem Fall die Methode eines Objekts ist, nicht die Referenz.
Da all dies nur zur Laufzeit gelöst wird und JVM zur Laufzeit erfährt, welche Methode aufgerufen werden muss, wird das Überschreiben von Methoden als dynamischer Polymorphismus oder einfach als Polymorphismus oder dynamische Bindung bezeichnet .
Sie können es mehr Details zu meinem Artikel lesen Wie funktioniert JVM Handle Methode Overloading und Übergeordnete Intern .