Eh bien, afin de comprendre comment la liaison statique et dynamique fonctionne réellement la ? ou comment ils sont identifiés par le compilateur et JVM?
Prenons l'exemple ci-dessous où se Mammal
trouve une classe parente qui a une méthode speak()
et une Human
classe étend Mammal
, remplace la speak()
méthode et la surcharge à nouveau 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
}
}
Lorsque nous compilons le code ci-dessus et essayons de regarder le bytecode en utilisant javap -verbose OverridingInternalExample
, nous pouvons voir que le compilateur génère une table constante où il affecte des codes entiers à chaque appel de méthode et code d'octet pour le programme que j'ai extrait et inclus dans le programme lui-même ( voir les commentaires ci-dessous chaque appel de méthode)
En regardant le code ci - dessus , nous pouvons voir que le bytecode de humanMammal.speak()
, human.speak()
et human.speak("Hindi")
sont totalement différents ( invokevirtual #4
, invokevirtual #7
, invokevirtual #9
) parce que le compilateur est capable de faire la différence entre eux en fonction de la liste des arguments et référence de classe. Parce que tout cela est résolu au moment de la compilation de manière statique, c'est pourquoi la surcharge de méthode est connue sous le nom de polymorphisme statique ou statique liaison statique .
Mais bytecode pour anyMammal.speak()
et humanMammal.speak()
est identique ( invokevirtual #4
) car selon le compilateur les deux méthodes sont appeléesMammal
référence.
Alors maintenant, la question se pose si les deux appels de méthode ont le même bytecode, comment JVM sait-elle quelle méthode appeler?
Eh bien, la réponse est cachée dans le bytecode lui-même et c'est invokevirtual
un jeu d'instructions. JVM utilise leinvokevirtual
instruction pour appeler l'équivalent Java des méthodes virtuelles C ++. En C ++, si nous voulons remplacer une méthode dans une autre classe, nous devons la déclarer comme virtuelle, mais en Java, toutes les méthodes sont virtuelles par défaut car nous pouvons remplacer toutes les méthodes de la classe enfant (à l'exception des méthodes privées, finales et statiques).
En Java, chaque variable de référence contient deux pointeurs cachés
- Un pointeur vers une table qui contient à nouveau les méthodes de l'objet et un pointeur vers l'objet Class. par exemple [speak (), speak (String) Class object]
- Un pointeur vers la mémoire allouée sur le tas pour les données de cet objet, par exemple les valeurs des variables d'instance.
Ainsi, toutes les références d'objet contiennent indirectement une référence à une table qui contient toutes les références de méthode de cet objet. Java a emprunté ce concept à C ++ et cette table est connue sous le nom de table virtuelle (vtable).
Une vtable est une structure de type tableau qui contient les noms de méthodes virtuelles et leurs références sur les index de tableau. JVM crée une seule vtable par classe lorsqu'elle charge la classe en mémoire.
Ainsi, chaque fois que JVM rencontre un invokevirtual
jeu d'instructions, il vérifie la vtable de cette classe pour la référence de méthode et invoque la méthode spécifique qui, dans notre cas, est la méthode d'un objet et non la référence.
Parce que tout cela est résolu au moment de l'exécution uniquement et au moment de l'exécution, la JVM sait quelle méthode appeler, c'est pourquoi le remplacement de méthode est connu sous le nom de polymorphisme dynamique ou simplement de polymorphisme ou de liaison dynamique. .
Vous pouvez le lire plus en détail sur mon article Comment la JVM gère-t-elle la surcharge et le remplacement de méthode en interne .