Calling principles of overloading and rewriting for java method calls (1)
Some time ago, I read the content of the third part of the virtual machine execution subsystem in "deep understanding JVM", and saw the calling principles of heavy load and rewriting at the JVM level (see the 8.3 method call section for details ), however, I did not write a blog summary. I will discuss it here. During the discussionBytecode instructionsRelated content.
Conclusion
1. overload method
The call to the overload method mainly depends on the static type, and the parameter method of the static type.
2. override Method
The calling of the rewrite method mainly depends on the actual type. If the actual type implements this method, the method is called directly. If the method is not implemented, search from low to high in the inheritance relationship is not implemented.
3.
The compilation process of java files does not involve the connection process of Traditional compilation.All method calls store only symbolic references in the class file, rather than the entry address in the memory layout of the method during actual running.
Basic Concepts
1. Static and actual types, method Receiver
Human man = new Man();man.foo();
In the preceding statement, the static type of man is Human, and the actual type is Man. The method receiver refers to the owner of the foo () method to be executed (in polymorphism, it may be the object of the parent class Human or the object of the subclass Man ).
2. bytecode method call command
(1) invokestatic: Call a static method
(2) invokespecial: Call the instance constructor method, private method, and parent class method.
(3) invokevirtual: Call all virtual methods.
(4) invokeinterface: Call an interface method and determine an object to implement this interface at runtime.
(5) invokedynamic: The method referenced by the call point qualifier is dynamically parsed at runtime, and then the method is executed.
The first two commands (invokestatic, invokespecial) can resolve the symbol reference to direct reference when the class is loaded, the static methods, instance constructor methods, private methods, and parent methods that meet this condition are called non-virtual methods.
Non-virtual MethodIn addition to the above static methods, instance constructor methods, private methods, and parent methodsFinal Method. Although the final method is called using the invokevirtual command, the final method cannot be overwritten, and there are no other versions. You do not need to perform multi-state selection on the method receiver, or the result of multi-state selection is unique.
Heavy Load
The static and dynamic types mentioned above can be changed. When the static type changes (forced type conversion), the compiler knows that the compiler knows the final static type of the object. When the actual type changes (the object points to another object), the compiler is unknown and can only be seen at runtime.
// Static type change sr. sayHello (Man) man); sr. sayHello (Woman) man); // actual type change Human man = new Man (); man = new Woman ();
The overload only applies to the selection of static types.
The test code is as follows:
/** * Created by fan on 2016/3/28. */public class StaticDispatcher { static 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) { StaticDispatcher staticDispatcher = new StaticDispatcher(); Human man = new Man(); Human woman = new Woman(); staticDispatcher.sayHello(man); staticDispatcher.sayHello(woman); staticDispatcher.sayHello((Man)man); staticDispatcher.sayHello((Woman)man); }}
First, let's look at the execution result:
It can be seen that when the static type changes, the method of the corresponding type is called. However, when the Man forced type is converted to Woman, there is no compilation error, but there is a runtime exception. "ClassCastException" class ing exception.
Look at the bytecode command:
Javap-verbZ success? Http://www.bkjia.com/kf/ware/vc/ "target =" _ blank "class =" keylink "> vc2uglwmgu3rhdgljrglzcgf0y2hlc1_vcd4ncjxwcmugy2xhc3m9" brush: java; ">public static void main(java.lang.String[]);Code:Stack=2, Locals=4, Args_size=10: new #7; //class StaticDispatcher3: dup4: invokespecial #8; //Method " ":()V7: astore_18: new #9; //class StaticDispatcher$Man11: dup12: invokespecial #10; //Method StaticDispatcher$Man." ":()V15: astore_216: new #11; //class StaticDispatcher$Woman19: dup20: invokespecial #12; //Method StaticDispatcher$Woman." ":()V23: astore_324: aload_125: aload_226: invokevirtual #13; //Method sayHello:(LStaticDispatcher$Human;)V29: aload_130: aload_331: invokevirtual #13; //Method sayHello:(LStaticDispatcher$Human;)V34: aload_135: aload_236: checkcast #9; //class StaticDispatcher$Man39: invokevirtual #14; //Method sayHello:(LStaticDispatcher$Man;)V42: aload_143: aload_244: checkcast #11; //class StaticDispatcher$Woman47: invokevirtual #15; //Method sayHello:(LStaticDispatcher$Woman;)V50: return
We can see that the command checkCast will be called during forced type conversion, and the call method of the invokevirtual command has also changed.39: invokevirtual #14; //Method sayHello:(LStaticDispatcher$Man;)V
.
When a VM (accurately called a compiler) is overloaded, it uses the static type of the parameter instead of the actual type as the judgment basis.
For literal types, the compiler automatically converts the types. The conversion sequence is as follows:
Char-int-long-float-long-Character-Serializable-Object
The Character is converted to Character because of automatic packing and to Serializable because Character implements the Serializable interface.
Override
The test code is as follows:
/** * Created by fan on 2016/3/29. */public class DynamicDispatcher { 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(); }}
Execution result:
Take a look at the bytecode command:
public static void main(java.lang.String[]); Code: Stack=2, Locals=3, Args_size=1 0: new #2; //class DynamicDispatcher$Man 3: dup 4: invokespecial #3; //Method DynamicDispatcher$Man."
":()V 7: astore_1 8: new #4; //class DynamicDispatcher$Woman 11: dup 12: invokespecial #5; //Method DynamicDispatcher$Woman."
":()V 15: astore_2 16: aload_1 17: invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V 20: aload_2 21: invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V 24: new #4; //class DynamicDispatcher$Woman 27: dup 28: invokespecial #5; //Method DynamicDispatcher$Woman."
":()V 31: astore_1 32: aload_1 33: invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V 36: return
From the bytecode, we can see that they call the same method.invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V
But the execution result shows that different methods are called. In the compilation phase, the compiler only knows the static type of the object, but does not know the actual type. Therefore, in the class file, it is only necessary to call the method of the parent class. However, the actual type of the object is determined during execution. If the actual type implements this method, it is called directly. If the method is not implemented, it is retrieved from the bottom up based on the inheritance relationship. If the method is not retrieved, throw an exception (Can it be compiled through).
(1) The test code is as follows:
/** * Created by fan on 2016/3/29. */public class Test { static class Human { protected void sayHello() { System.out.println("Human say hello"); } protected void sayHehe() { System.out.println("Human say hehe"); } } static class Man extends Human { @Override protected void sayHello() { System.out.println("Man say hello"); }// protected void sayHehe() {// System.out.println("Man say hehe");// } } static class Woman extends Human { @Override protected void sayHello() { System.out.println("Woman say hello"); }// protected void sayHehe() {// System.out.println("Woman say hehe");// } } public static void main(String[] args) { Human man = new Man(); man.sayHehe(); }}
The test results are as follows:
Bytecode instructions:
public static void main(java.lang.String[]); Code: Stack=2, Locals=2, Args_size=1 0: new #2; //class Test$Man 3: dup 4: invokespecial #3; //Method Test$Man."
":()V 7: astore_1 8: aload_1 9: invokevirtual #4; //Method Test$Human.sayHehe:()V 12: return
The bytecode command is essentially different from the bytecode command in the above Code.
(2) The test code is as follows:
/** * Created by fan on 2016/3/29. */public class Test { static class Human { protected void sayHello() { } } static class Man extends Human { @Override protected void sayHello() { System.out.println("Man say hello"); } protected void sayHehe() { System.out.println("Man say hehe"); } } static class Woman extends Human { @Override protected void sayHello() { System.out.println("Woman say hello"); } protected void sayHehe() { System.out.println("Woman say hehe"); } } public static void main(String[] args) { Human man = new Man(); man.sayHehe(); }}
Compilation error:
This example illustrates:The Java compiler performs checks based on static types.
Modify the Error Code as follows:
/** * Created by fan on 2016/3/29. */public class Test { static class Human { protected void sayHello() { System.out.println("Human say hello"); }// protected void sayHehe() {// System.out.println("Human say hehe");// } } static class Man extends Human { @Override protected void sayHello() { System.out.println("Man say hello"); } protected void sayHehe() { System.out.println("Man say hehe"); } } static class Woman extends Human { @Override protected void sayHello() { System.out.println("Woman say hello"); } protected void sayHehe() { System.out.println("Woman say hehe"); } } public static void main(String[] args) { Man man = new Man(); man.sayHehe(); }}
Note that the Main method is changedMan man = new Man();
The execution result is as follows:
The bytecode command is as follows:
public static void main(java.lang.String[]); Code: Stack=2, Locals=2, Args_size=1 0: new #2; //class Test$Man 3: dup 4: invokespecial #3; //Method Test$Man."
":()V 7: astore_1 8: aload_1 9: invokevirtual #4; //Method Test$Man.sayHehe:()V 12: return
Note the above bytecode instructionsinvokevirtual #4; //Method Test$Man.sayHehe:()V
.
Conclusion
This article discusses the basic principles of heavy load and rewriting, and views the relevant bytecode commands. The next blog post is about single dispatch and multi-dispatch for java method calls (2) about single dispatch and multi-Dispatch.
References: Zhou Zhiming deep understanding of Java Virtual Machine