web-dev-qa-db-fra.com

Pourquoi invokeSpecial est nécessaire lorsqu'il existe invokeVirtual

Il existe trois opcodes pour appeler des méthodes Java. Il est clair que invokeStatic ne concerne que l’appel de méthode statique.

Autant que je sache, invokespecial est utilisé lors de l'appel de méthodes de constructeur et de méthodes privées. Alors, avons-nous besoin de différencier les invocations de méthodes privées et publiques au moment de l'exécution? Il pourrait être invoqué avec le même opcode dire invokevirtual?

JVM traite-t-il des définitions de méthodes privées et publiques? Pour autant que je sache, des mots clés publics et privés sont-ils nécessaires en phase de développement pour l'encapsulation?

44
Ahmet Karakaya

http://www.artima.com/underthehood/invocationP.html Le lien ci-dessus donne clairement des exemples précieux qui répondent à ma question.

class Superclass {

    private void interestingMethod() {
        System.out.println("Superclass's interesting method.");
    }

    void exampleMethod() {
        interestingMethod();
    }
}

class Subclass extends Superclass {

    void interestingMethod() {
        System.out.println("Subclass's interesting method.");
    }

    public static void main(String args[]) {
        Subclass me = new Subclass();
        me.exampleMethod();
    }
}

Lorsque vous appelez main () dans la sous-classe définie ci-dessus, il doit afficher "la méthode intéressante de la superclasse". Si invokevirtual était utilisé, il imprimerait "la méthode intéressante de la sous-classe". Pourquoi? Parce que la machine virtuelle choisirait le intéressant intéressant Méthodes () à appeler en fonction de la classe réelle de l'objet, à savoir Subclass. Donc, il utilisera interestMethod () de la classe. D'un autre côté, avec invokespecial, la machine virtuelle sélectionnera la méthode en fonction du type de référence, de sorte que la version de Superclass de intéressantMethod () sera invoquée. 

20
Ahmet Karakaya

De ce site

La réponse peut être facilement trouvée si l’on lit attentivement la spécification Java VM:

La différence entre les instructions invokespecial et invokevirtual réside dans le fait qu'invokevirtual appelle une méthode basée sur la classe de l'objet. L'instruction invokespecial est utilisée pour appeler des méthodes d'initialisation d'instance, ainsi que des méthodes privées et des méthodes d'une superclasse de la classe actuelle.

En d’autres termes, invokespecial est utilisé pour appeler des méthodes sans se soucier de la liaison dynamique, afin d’appeler la version d’une classe donnée.

33
Tim Pote

Merci d'avoir lu cette explication: N'oubliez pas de passer au vote supérieur si cela vous aide à identifier la création d'une instruction d'assemblage pendant l'appel de la méthode . J'explique ici la liaison statique vs dynamique.

Tout d'abord, je vais vous dire que invokeStatic, invokeSpecial, invokeVirtual, invokeInterface etc sont les instructions d'assembly qui sont générées par le compilateur après le processus de compilation. Comme nous le savons tous, nous avons obtenu un. format de fichier de classe après la compilation et nous ne pouvons pas le lire. Mais Java fournit un outil pour cela nommé "javap" .

Nous pouvons lire les instructions d'assemblage de notre fichier .class à l'aide de la commande javap. Par défaut, nous ne pouvons pas voir les instructions de la méthode private avec Assembly, nous devons donc utiliser -private avec. Vous trouverez ci-dessous les commandes permettant d'afficher les instructions d'assemblage générées par le compilateur Java:

  1. Imaging vous avez classe A.Java

    class A { public void printValue () { System.out.println ("Inside A"); }

    méthode statique publique d'appel void (A a) { a.printValue (); } }

  2. ouvrez cmd Prompt et accédez au dossier contenant ce fichier Java A.Java.

  3. exécutez javac A.Java.

  4. Maintenant, le fichier A.class est généré. Il contient des instructions d'assemblage, mais vous ne pouvez pas le lire.

  5. Maintenant, lancez javap -c A

  6. Vous pouvez voir la génération d'assemblage pour votre appel de méthode -> a.printValue ();

  7. Si la méthode printValue () est privée, vous devez utiliser javap -c -private A.

  8. Vous pouvez rendre votre printValue () privé/statique/public/privé statique à la fois.

  9. Une dernière chose à garder à l’esprit que le premier compilateur vérifie l’objet sur lequel la méthode est appelée. Trouvez ensuite son type de classe et trouvez cette méthode dans cette classe, qu'elle soit disponible ou non.

Remarque: Maintenant, gardez à l'esprit que si notre méthode d'appel est statique, invokeStatic Assembly est générée, si son instruction privée, invokeSpecial Assembly est générée et si son instruction publique, invokeVirtual est générée. méthode publique ne signifie jamais que chaque fois que l'instruction invokeVirtual est générée. Dans le cas de super.printValue (), l’appel de la sous-classe de A est un cas exceptionnel. C'est-à-dire que si A est la classe mère de B et que B contient la même méthode printValue (), elle générera alors invokeVirtual (dynamic), mais si printValue () dans B a super.printValue () comme première instruction, invokeStatic est généré même si printValue () ) de A est public.

Essayons ça aussi:

class B extends A
{
public void printValue()
{
super.printValue();// invokeStatic
System.out.println("Inside B");
}

}

public class Test
{
public static void main(String[] arr)
{
    A a = new A();
    B b = new B();
    A.callMethod(a);// invokeVirtual
    A.callMethod(b);// invokeVirtual
}
}

-> enregistrez-le par Test.Java -> lancez javac Test.Java -> javap -c -private Test

0
Maddy