62

There are three opcodes to invoke Java methods. It is clear that invokeStatic is just for static method invocation.

As far as I know invokespecial is used when invoking constructor and private methods. So, do we need to differenticate private and public method invocation at run time? It could be invoked with same opcode say invokevirtual?

Does JVM deals with private and public method definition? As far as I know public and private keywords is just needed at development phase for encapsulation?

halfer
  • 19,824
  • 17
  • 99
  • 186
Ahmet Karakaya
  • 9,899
  • 23
  • 86
  • 141
  • As per you comment, there could be scenario in which invokespecial is required while invoking private method. – Ahmet Karakaya Dec 07 '12 at 13:55
  • possible duplicate of [Method invocation of invokespecial and invokeinterface](http://stackoverflow.com/questions/13721111/method-invocation-of-invokespecial-and-invokeinterface) – Ian Roberts Dec 07 '12 at 13:57

3 Answers3

50

From this site

The answer can be easily found if one reads the Java VM Spec carefully:

The difference between the invokespecial and the invokevirtual instructions is that invokevirtual invokes a method based on the class of the object. The invokespecial instruction is used to invoke instance initialization methods as well as private methods and methods of a superclass of the current class.

In other words, invokespecial is used to call methods without concern for dynamic binding, in order to invoke the particular class’ version of a method.

There are several cases where its important to dispatch a method call "without concern for dynamic binding":

First, when chaining from one constructor to a super-class constructor.

Second, as in super.foo() when calling from a method to a super-class's implementation. If the class, or any sub-class of it overrode that method, then invokevirtual would go to the wrong place.

Third, when a class wants to pick which default version of a method to use as in

interface I { void f(); }
interface J extends I { default void f() { ... } }
interface K extends I { default void f() { ... } }
class C implements J, K {
  @Override public void f() { K.super.f(); }
}

class C above has a diamond inheritance problem, so it needs to pick which default method to invoke. invokespecial allows it to dispatch to K's version, but if K.super.f dispatched via invokevirtual the call would end up back at C.f.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
Tim Pote
  • 27,191
  • 6
  • 63
  • 65
33

http://www.artima.com/underthehood/invocationP.html The link above gives valuable examples clearly which addresing my 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();
    }
}

When you invoke main() in Subclass as defined above, it must print "Superclass's interesting method." If invokevirtual were used, it would print "Subclass's interesting method." Why? Because the virtual machine would choose the interestingMethod() to call based on the actual class of the object, which is Subclass. So it will use Subclass's interestingMethod(). On the other hand, with invokespecial the virtual machine will select the method based on the type of the reference, so Superclass's version of interestingMethod() will be invoked.

wchargin
  • 15,589
  • 12
  • 71
  • 110
Ahmet Karakaya
  • 9,899
  • 23
  • 86
  • 141
  • 4
    I can think of no good reason to have code like this. This is [*hiding*](http://docs.oracle.com/javase/tutorial/java/IandI/override.html), and any good IDE will give you a warning or error if you do this. The subtle difference between the two `interestingMethod` signatures is enough to confuse the reader as to what's happening. – wchargin Aug 10 '13 at 16:17
  • 7
    @wchargin For example, this might happen when Superclass is a library class that you extend. Maybe you don't even have its source code. You certainly don't care about its private methods. It's quite possible that you add a method to Subclass that happens to have the same name as a private method in Superclass. – jcsahnwaldt Reinstate Monica Oct 30 '16 at 01:19
  • 2
    For other readers: Also keep in mind that the Java _language_ has rules and restrictions that the _JVM_ does not. The above is a very good example of "what kind of source code might produce the kind of bytecode being discussed," but that doesn't mean it's good software engineering. :-) For that matter, such source code wouldn't have to be in Java, merely some language that can be compiled to JVM bytecode. – Ti Strga Feb 21 '18 at 17:08
  • 1
    I thought the same that 'Had it been `invokevirtual` when Superclass calls `interestingMethod()` from `exampleMethod()`. But actually did an experiment by modifying bytecode in `Superclass.class` file by replacing `invokespecial` with `invokevirtual`, the output from running `Java Subclass` is still "Superclass's interesting method." Also, found that JDK 11 actually compiles Superclass with bytecode instruction `invokevirtual` directly! While JDK 8 still compiles with `invokespecial` – Alfred Xiao Sep 06 '20 at 10:30
  • So I think it does NOT matter in this particular case wither it is `invokespecial` or `invokevirtual`, because the method being called is a private function, which JVM will check and make a decision to not dispatch based on class. – Alfred Xiao Sep 06 '20 at 10:38
1

Thanks for reading out that explanation: Please don't forget to upvote if it helps you out to identify assembly instruction creation during method call Here I am explaining static vs dynamic binding.

First of all I am going to tell you that invokeStatic, invokeSpecial, invokeVirtual, invokeInterface etc are the assembly instructions which are generate by the compiler after compilation process. As we all knows that we got a .class file format after compilation and we can't read it out. But java provide a tool for that named "javap".

We can read out our .class file assembly instructions by using javap command. By default we can't see private method assembly instructions so we need to use -private with it. Below are the commands to see java compiler generated assembly instructions:

  1. Imaging you have A.java class

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

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

  2. open cmd prompt and go to the folder which contains that java file A.java.

  3. run javac A.java.

  4. Now A.class file is generated which contains assembly instructions but you can't read it.

  5. Now run javap -c A

  6. You can see assembly generation for your method call --> a.printValue();

  7. If printValue( ) method is private you need to use javap -c -private A .

  8. You can make your printValue( ) private / static / public / private static both.

  9. One more thing to keep in mind that first compiler check the object on which the method is getting called. Then find its Class Type and found that method in that class if available or not.

Note : Now keep in mind that if our calling method is static then invokeStatic assembly is generated, if its private then invokeSpecial assembly instruction is generated and if its public then invokeVirtual instruction is generated. public method never mean that every time invokeVirtual instruction is generated. In case of super.printValue() call from the subclass of A is exceptional case. i.e. if A is parent class for B and B contains the same method printValue() then it will generate invokeVirtual(dynamic) but if printValue() in B have super.printValue() as its first statement then invokeStatic is generated even if printValue() of A is public.

Let's try this too:

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

--> save it by Test.java --> run javac Test.java --> javap -c -private Test

Maddy Sharma
  • 4,870
  • 2
  • 34
  • 40