The three main features of JAVA are inheritance, encapsulation, and polymorphism. The dispatch call process will reveal some of the most basic manifestations of polymorphism, such as override and overload.
1. Static Assignment
Before introducing static assignment, let's look at a piece of code
public class StaticDispatch { static abstract class Human{ } static class Man extends Human{ } static class Woman extends Human{ } public void sayHello(Human human){ System.out.println("hello,guy"); } public void sayHello(Man man){ System.out.println("hello,man"); } public void sayHello(Woman woman){ System.out.println("hello,woman"); } public static void main(String[] args) { Human man = new Man(); Human woman = new Woman(); StaticDispatch dispatch = new StaticDispatch(); dispatch.sayHello(man); dispatch.sayHello(woman); } }
Output results:
hello,guy
hello,guy
Why does a virtual machine execute an overloaded version with the parameter Human? Why not analyze this line of code first?
Human man = new Man();
Here Human is called the static type (or appearance type) of the man object, and Man is called the actual type (or runtime type) of the variable.Static types and actual types may change at runtime, but changes to static types occur only when they are in use, static types of variables themselves are not changed, and the final static type is known at compile time, while changes to actual types can only be determined at runtime.
For overloaded methods, the virtual machine relies on the static type of the parameter as the static type is known to the compiler and the JVM needs to determine which overloaded method to call at compile time, obviously not the actual type that will only be determined at run time.
Therefore, all assignment actions that rely on static types to determine the version of a method's execution are referred to as static assignments.
2. Dynamic Assignment
Let's start with the next piece of code
public class DynamicDispatch { static abstract class Human{ protected abstract void sayHello(); } static class Man extends Human{ @Override protected void sayHello() { System.out.println("man say hello"); } } static class Woman extends Human{ @Override protected void sayHello() { System.out.println("woman say hello"); } } public static void main(String[] args) { Human man = new Man(); Human woman = new Woman(); man.sayHello(); woman.sayHello(); man = new Woman(); man.sayHello(); } }
Output results:
man say hello
woman say hello
woman say hello
As a result, the call to sayHello() is determined by the actual type of the variable, because at this point sayHello()The method parameter list of the method is the same and does not constitute an overloaded method. From the decompiled byte code instruction, the method should be invoked after the corresponding object is obtained, that is, Figures 1617 and 2021 below, the man and woman objects are loaded, and then the method is invoked.
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #2 // class com/cry/spring/Extend/DynamicDispatch$Man 3: dup 4: invokespecial #3 // Method com/cry/spring/Extend/DynamicDispatch$Man."<init>":()V 7: astore_1 8: new #4 // class com/cry/spring/Extend/DynamicDispatch$Woman 11: dup 12: invokespecial #5 // Method com/cry/spring/Extend/DynamicDispatch$Woman."<init>":()V 15: astore_2 16: aload_1 17: invokevirtual #6 // Method com/cry/spring/Extend/DynamicDispatch$Human.sayHello:()V 20: aload_2 21: invokevirtual #6 // Method com/cry/spring/Extend/DynamicDispatch$Human.sayHello:()V 24: new #4 // class com/cry/spring/Extend/DynamicDispatch$Woman 27: dup 28: invokespecial #5 // Method com/cry/spring/Extend/DynamicDispatch$Woman."<init>":()V 31: astore_1 32: aload_1 33: invokevirtual #6 // Method com/cry/spring/Extend/DynamicDispatch$Human.sayHello:()V 36: return
Unlike previous StaticDispatches, in StaticDispatch, the objects calling sayHello were dispatch es, and the objects calling methods in DynamicDispatch were two different objects, so different results would occur.
More recently, the invokevirtual directive, which resolves at run time, plays a major role in dynamic assignment:
1) Find the actual type of object that the first element on the top of the operand stack points to, and mark it as C.
2) If a method matching both the descriptor and the simple name is found in Class C, then the method privilege check is performed. If there is access privilege, the direct reference to the method is returned, and the lookup process ends; otherwise, an IllegalAccessError exception is thrown.
3) Otherwise, search and validate each parent of C in turn from bottom to top according to the inheritance relationship.
4) Throw an AbstractMethodError exception if no suitable method is found.
However, dynamic assignment is only valid for methods and not for fields.
public class FieldHasNoPolymorphic { static class Father{ int money =1; } static class Son extends Father{ int money =2; } public static void main(String[] args) { Father guy = new Son(); System.out.println(guy.money); } }
Output result is 1
That is, even if the subclass defines money, it will not be affected by dynamic assignment, because the field will not use the invokeDynamic directive, nor will it make virtual, in other words, the field will never participate in polymorphism.