web-dev-qa-db-de.com

Warum ist invokeSpecial erforderlich, wenn invokeVirtual vorhanden ist

Es gibt drei Opcodes, um Java-Methoden aufzurufen. Es ist klar, dass invokeStatic nur für das Aufrufen statischer Methoden ist.

Soweit ich weiß, wird invokespecial beim Aufruf von Konstruktor- und privaten Methoden verwendet. Müssen wir also den Aufruf von privaten und öffentlichen Methoden zur Laufzeit unterscheiden? Es könnte mit demselben Opcode aufgerufen werden, beispielsweise invokevirtual?

Befasst sich die JVM mit der Definition privater und öffentlicher Methoden? Soweit ich weiß, werden öffentliche und private Keywords nur in der Entwicklungsphase für die Einkapselung benötigt?

44
Ahmet Karakaya

http://www.artima.com/underthehood/invocationP.html Der Link oben gibt wertvolle Beispiele, die meine Frage aufwerfen.

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();
    }
}

Wenn Sie main () in der Unterklasse wie oben definiert aufrufen, muss "Interessante Methode der Superklasse" gedruckt werden. Wenn invokevirtual verwendet wird, wird die interessante Methode der Unterklasse gedruckt. Warum? Weil die virtuelle Maschine die aufrufende interessanteMethod () basierend auf der tatsächlichen Klasse des Objekts (Unterklasse) auswählen würde. Es wird also die interessante Methode von Subclass verwendet. Andererseits wählt die virtuelle Maschine mit invokespecial die Methode basierend auf dem Typ der Referenz aus, sodass die Version von interestMethod () von Superclass aufgerufen wird. 

20
Ahmet Karakaya

Von dieser Seite

Die Antwort kann leicht gefunden werden, wenn man die Java VM Spec sorgfältig liest:

Der Unterschied zwischen den invokespezifischen und den invokevirtuellen Anweisungen besteht darin, dass invokevirtual eine Methode auf der Grundlage der Klasse des Objekts aufruft. Die invokespecial-Anweisung wird verwendet, um Instanzeninitialisierungsmethoden sowie private Methoden und Methoden einer Superklasse der aktuellen Klasse aufzurufen.

Mit anderen Worten, invokespecial wird verwendet, um Methoden ohne Bedenken hinsichtlich der dynamischen Bindung aufzurufen, um die jeweilige Klassenversion einer Methode aufzurufen.

33
Tim Pote

Vielen Dank, dass Sie diese Erklärung vorgelesen haben: Vergessen Sie nicht, zu stimmen, wenn Sie dabei helfen, die Erstellung von Assembly-Anweisungen während des Methodenaufrufs Zu erkennen. Hier erkläre ich die statische vs. dynamische Bindung.

Zunächst möchte ich Ihnen sagen, dass invokeStatic, invokeSpecial, invokeVirtual, invokeInterface etc die Assembly-Anweisungen sind, die vom Compiler nach dem Kompilierungsvorgang generiert werden. Wie wir alle wissen, haben wir eine. Klassendateiformat nach der Kompilierung und wir können es nicht auslesen. Java bietet jedoch ein Werkzeug für den Namen "javap" .

Wir können unsere .class-Datei mit dem Befehl javap auslesen. Standardmäßig sehen wir keine privaten Montageanweisungen. Daher müssen Sie -private verwenden. Im Folgenden finden Sie die Befehle zum Anzeigen der von einem Java-Compiler generierten Assembly-Anweisungen:

  1. Imaging haben Sie A.Java-Klasse

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

    public static void callMethod (A a) { a.printValue (); } }

  2. Öffnen Sie die Eingabeaufforderung von cmd, und wechseln Sie zu dem Ordner, der die Java-Datei A.Java enthält.

  3. führen Sie Javac A.Java.

  4. Jetzt wird eine A.class-Datei generiert, die Assembly-Anweisungen enthält, die Sie jedoch nicht lesen können.

  5. Führen Sie nun javap -c A aus

  6. Sie können Assembly-Generierung für Ihren Methodenaufruf sehen -> a.printValue ();

  7. Wenn die Methode printValue () privat ist, müssen Sie javap -c -private A verwenden.

  8. Sie können sowohl printValue () als privat/statisch/öffentlich/privat statisch machen.

  9. Beachten Sie, dass der erste Compiler das Objekt überprüft, für das die Methode aufgerufen wird. Suchen Sie dann seinen Klassentyp und finden Sie diese Methode in dieser Klasse, falls verfügbar oder nicht.

Hinweis: Denken Sie daran, dass, wenn unsere Aufrufmethode statisch ist, die invokeStatic Assembly generiert wird, wenn ihre private dann die Anweisung invokeSpecial Assembly generiert wird und wenn die öffentliche Anweisung dann die invokeVirtual-Anweisung generiert. Eine öffentliche Methode bedeutet nicht, dass jedes Mal eine invokeVirtual-Anweisung generiert wird. Im Falle von super.printValue () ist der Aufruf aus der Unterklasse von A ein Ausnahmefall. dh wenn A die übergeordnete Klasse für B ist und B dieselbe Methode printValue () enthält, wird invokeVirtual (dynamisch) generiert. Wenn jedoch printValue () in B die erste Anweisung super.printValue () hat, wird invokeStatic selbst dann generiert, wenn printValue ( ) von A ist öffentlich.

Lass uns das auch versuchen:

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
}
}

-> speichere es mit Test.Java -> javac ausführen Test.Java -> javap -c -private Test

0
Maddy