Home  >  Article  >  Java  >  Introduction to static dispatch and dynamic dispatch in Java (code example)

Introduction to static dispatch and dynamic dispatch in Java (code example)

不言
不言forward
2019-02-19 15:56:332041browse

This article brings you an introduction to static dispatch and dynamic dispatch in Java (code examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Recently reviewing the knowledge of JVM, the understanding of static dispatch and dynamic dispatch is a bit confusing, so I tried to write the code myself and consolidate the knowledge in the analysis.

There is the following piece of code. What does each piece of code output?

package com.khlin.my.test;

class Base {

    public static void foo() {
        System.out.println("Base.foo() invoked");
    }

    public void bar(int c) {
        System.out.println("Base.bar(int) invoked");
    }

    public void bar(Character c) {
        System.out.println("Base.bar(Character) invoked");
    }

    public void baz(Object o) {
        System.out.println("Base.baz(Object) invoked");
    }

    public void baz(Integer i) {
        System.out.println("Base.baz(Integer) invoked");
    }

}

class Child extends Base {
    public static void foo() {
        System.out.println("Child.foo() invoked");
    }

    public void bar(Character c) {
        System.out.println("Child.bar(Character) invoked");
    }

    public void bar(char c) {
        System.out.println("Child.bar(char) invoked");
    }
}

public class App {

    public static void main(String[] args) {
        Base child = new Child();

        System.out.println("第1段输出:");
        child.foo();
        child.bar(new Character('C'));

        System.out.println("第2段输出:");
        Object integer = new Integer(100);
        child.baz(integer);

        System.out.println("第3段输出:");
        child.bar('C');

    }
}

Let me briefly introduce the entire process from code compilation to method calling.

· Compile

First look at the output of the first paragraph. Is child.foo() calling a static method of the parent class or the subclass? ?

During the compilation phase, static dispatch occurs.

1 Base child = new Child();

When we create an object, as shown above, Base is called the static type of the variable (Static Type), or it is called the appearance type (Apparent Type) , the following Child is called the actual type of the variable (Actual Type).

All dispatch actions that rely on static types to locate the method execution version are called static dispatch. A typical application of static dispatch is method overloading, which occurs during the compilation phase, so the action determined to be statically dispatched is not actually executed by the virtual machine.

The receiver (Reciever) of the method and the parameters of the method are collectively called the method's variables. Depending on how many types of variables the dispatch is based on, dispatch can be divided into single dispatch and multi-dispatch.

In static dispatch, there are two bases for selecting the target method. One is whether the static type is Base or Child, and the other is the parameter type of the method. Therefore, static dispatch is multi-dispatch.

Next, let’s take a look at the instructions generated by the “output section 1” code. The following results are obtained through the javap -v App.class instruction. You can see the symbolic references of the two instructions on lines 18 and 31, which are consistent with the above analysis: the static type of child is Base, so the method of the Base class is selected; through None The parameter and Character types respectively determine which method version it is.

But in the end the behavior of the two is different. child.foo() calls foo() of the static type Base, while child.bar(new Character('C ')) is to call the method of the actual type Child.

The reason is that the two instructions are different: invokestatic and invokevirtual

The Java virtual machine provides 5 method calling bytecode instructions:

invokestatic: call Static method

invokespecial: Invoke instance constructor 7e51f00a783d7eb8f68358439dee7daf method, private method and parent class method

invokevirtual: Invoke all virtual methods

invokeinterface: Invoke interface method , an object that implements this interface will be determined at runtime

invokedynamic: First, dynamically parse out the method referenced by the call point qualifier at runtime, and then execute the method. The previous 4 calls Instructions and dispatch logic are solidified inside the Java virtual machine, and invokedynamic is determined by the boot method set by the user.

The specific reason is that different instructions behave differently in the next stage (parsing of class loading). Let’s put it aside for now, and let’s look at the instructions output in the second paragraph.

It can be seen that In static dispatch, the method version to be called is determined based on the static type of the parameters passed in to the method, Although there is a baz(Integer) method, the static type of the integer parameter passed in is Object, so baz(Object) is called.

Let’s look at the instructions output in paragraph 3. We know that the symbol reference must still be a method in the Base class (although there is a bar(char c) method with the same parameters in the Child class), but there are no identical parameters in Base. (char type) method, won’t an error be reported? Which method will be called?

It turns out that although the compiler can determine the overloaded version of a method, in many cases this overloaded version is not "the only one" and can often only determine one. A "more appropriate" version.

·Class loading parsing

The parsing phase is the process in which the virtual machine replaces the symbol references in the constant pool with direct references.

As long as the method can be called by the invokestatic and invokespecial instructions, the only calling version can be determined in the parsing phase. The four categories that meet this condition are static methods, private methods, instance constructors, and parent class methods. They will resolve the symbol reference to a direct reference to the method when the class is loaded. These methods can be called non-virtual methods, and other methods are called non-virtual methods (except final methods).

Although the final modified method is called using the invokevirtual instruction, since it cannot be overwritten and there is no other version, it is also a non-virtual method.

Go back to the first paragraph of output. child.foo() is an invokestatic instruction, so during the parsing stage, it will be replaced with a direct reference, and the specific class will be determined, so the static type Base.foo() is called. ).

And child.bar(new Character('C')) is invokevirtual. At this stage, the signature of the called method can be determined, but the actual type of the method's receiver cannot yet be determined. It will be determined by dynamic dispatch. Dynamic allocation is single allocation because there is only one volume effect.

#The actual type of the method receiver is determined in the next stage.

·Method call during runtime

The execution version of the method is determined based on the actual type during runtime The dispatch process is called dynamic dispatch.

The final output is:

The above is the detailed content of Introduction to static dispatch and dynamic dispatch in Java (code example). For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete