The execution of a Java program requires two steps: compilation and execution (interpretation). At the same time, Java is an object-oriented programming language. When the subclass and the parent class have the same method, and the subclass overrides the method of the parent class, when the program calls the method at runtime, should it call the method of the parent class or the overridden method of the subclass? This should be the question when we first learn Java. problems encountered. Here first we will determine which method to call or the operation of variables is called binding.
There are two binding methods in Java, one is static binding, also called early binding. The other is dynamic binding, also known as late binding.
Difference comparison
1. Static binding occurs at compile time, and dynamic binding occurs at runtime.
2. Use variables or methods modified with private, static or final, and use static binding. Virtual methods (methods that can be overridden by subclasses) will be dynamically bound based on the runtime object.
3. Static binding is completed using class information, while dynamic binding needs to be completed using object information.
4. The overloaded method is completed using static binding, while the overriding method is completed using dynamic binding.
Example of overloaded method
Here is an example of overloaded methods.
Copy the code code as follows:
public class TestMain {
public static void main(String[] args) {
String str = new String();
Caller caller = new Caller();
caller.call(str);
}
static class Caller {
public void call(Object obj) {
System.out.println("an Object instance in Caller");
}
public void call(String str) {
System.out.println("a String instance in in Caller");
}
}
}
The execution result is
Copy the code code as follows:
22:19 $javaTestMain
a String instance in in Caller
In the above code, there are two overloaded implementations of the call method. One receives an object of type Object as a parameter, and the other receives an object of type String as a parameter. str is a String object, and all call methods that receive String type parameters will be called. The binding here is static binding based on the parameter type at compile time.
verify
Just looking at the appearance cannot prove that static binding is performed. You can verify it by using javap to compile it.
Copy the code code as follows:
22:19 $ javap -c TestMain
Compiled from "TestMain.java"
public class TestMain {
public TestMain();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/String
3: dup
4: invokespecial #3 // Method java/lang/String."<init>":()V
7: astore_1
8: new #4 // class TestMain$Caller
11: dup
12: invokespecial #5 // Method TestMain$Caller."<init>":()V
15: astore_2
16: aload_2
17: aload_1
18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V
21: return
}
I saw this line 18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V is indeed statically bound, which confirms that the caller method that receives a String object as a parameter is called.
Example of overriding a method
Copy the code code as follows:
public class TestMain {
public static void main(String[] args) {
String str = new String();
Caller caller = new SubCaller();
caller.call(str);
}
static class Caller {
public void call(String str) {
System.out.println("a String instance in Caller");
}
}
static class SubCaller extends Caller {
@Override
public void call(String str) {
System.out.println("a String instance in SubCaller");
}
}
}
The execution result is
Copy the code code as follows:
22:27 $javaTestMain
a String instance in SubCaller
In the above code, there is an implementation of the call method in Caller. SubCaller inherits Caller and rewrites the implementation of the call method. We declared a variable callerSub of type Caller, but this variable points to a SubCaller object. According to the results, it can be seen that it calls the call method implementation of SubCaller instead of the call method of Caller. The reason for this result is that dynamic binding occurs at runtime, and during the binding process it is necessary to determine which version of the call method implementation to call.
verify
Dynamic binding cannot be directly verified using javap, and if it is proved that static binding is not performed, it means that dynamic binding is performed.
Copy the code code as follows:
22:27 $ javap -c TestMain
Compiled from "TestMain.java"
public class TestMain {
public TestMain();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/String
3: dup
4: invokespecial #3 // Method java/lang/String."<init>":()V
7: astore_1
8: new #4 // class TestMain$SubCaller
11: dup
12: invokespecial #5 // Method TestMain$SubCaller."<init>":()V
15: astore_2
16: aload_2
17: aload_1
18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V
21: return
}
As the result above, 18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V This is TestMain$Caller.call instead of TestMain$SubCaller.call, because the calling subroutine cannot be determined at compile time The class is still the implementation of the parent class, so it can only be handled by dynamic binding at runtime.
When reloading meets rewriting
The following example is a bit abnormal. There are two overloads of the call method in the Caller class. What is more complicated is that SubCaller integrates Caller and overrides these two methods. In fact, this situation is a compound situation of the above two situations.
The following code will first perform static binding to determine the call method whose parameter is a String object, and then perform dynamic binding at runtime to determine whether to execute the call implementation of the subclass or the parent class.
Copy the code code as follows:
public class TestMain {
public static void main(String[] args) {
String str = new String();
Caller callerSub = new SubCaller();
callerSub.call(str);
}
static class Caller {
public void call(Object obj) {
System.out.println("an Object instance in Caller");
}
public void call(String str) {
System.out.println("a String instance in in Caller");
}
}
static class SubCaller extends Caller {
@Override
public void call(Object obj) {
System.out.println("an Object instance in SubCaller");
}
@Override
public void call(String str) {
System.out.println("a String instance in in SubCaller");
}
}
}
The execution result is
Copy the code code as follows:
22:30 $javaTestMain
a String instance in in SubCaller
verify
Since it has been introduced above, I will only post the decompilation results here.
Copy the code code as follows:
22:30 $ javap -c TestMain
Compiled from "TestMain.java"
public class TestMain {
public TestMain();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/String
3: dup
4: invokespecial #3 // Method java/lang/String."<init>":()V
7: astore_1
8: new #4 // class TestMain$SubCaller
11: dup
12: invokespecial #5 // Method TestMain$SubCaller."<init>":()V
15: astore_2
16: aload_2
17: aload_1
18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V
21: return
}
Curious questions
Isn't it possible to use dynamic binding?
In fact, in theory, the binding of certain methods can also be achieved by static binding. for example:
Copy the code code as follows:
public static void main(String[] args) {
String str = new String();
final Caller callerSub = new SubCaller();
callerSub.call(str);
}
For example, here callerSub holds the object of subCaller and the callerSub variable is final, and the call method is executed immediately. In theory, the compiler can know that the call method of SubCaller should be called by sufficient analysis of the code.
But why is there no static binding?
Assume that our Caller inherits from the BaseCaller class of a certain framework, which implements the call method, and BaseCaller inherits from SuperCaller. The call method is also implemented in SuperCaller.
Assume that BaseCaller and SuperCaller in a certain framework 1.0
Copy the code code as follows:
static class SuperCaller {
public void call(Object obj) {
System.out.println("an Object instance in SuperCaller");
}
}
static class BaseCaller extends SuperCaller {
public void call(Object obj) {
System.out.println("an Object instance in BaseCaller");
}
}
We implemented this using framework 1.0. Caller inherits from BaseCaller and calls the super.call method.
Copy the code code as follows:
public class TestMain {
public static void main(String[] args) {
Object obj = new Object();
SuperCaller callerSub = new SubCaller();
callerSub.call(obj);
}
static class Caller extends BaseCaller{
public void call(Object obj) {
System.out.println("an Object instance in Caller");
super.call(obj);
}
public void call(String str) {
System.out.println("a String instance in in Caller");
}
}
static class SubCaller extends Caller {
@Override
public void call(Object obj) {
System.out.println("an Object instance in SubCaller");
}
@Override
public void call(String str) {
System.out.println("a String instance in in SubCaller");
}
}
}
Then we compiled the class file based on version 1.0 of this framework. Assuming that static binding can determine that the super.call of the above Caller is implemented as BaseCaller.call.
Then we assume again that BaseCaller does not rewrite the call method of SuperCaller in version 1.1 of this framework. Then the above assumption that the call implementation that can be statically bound will cause problems in version 1.1, because super.call should use SuperCall in version 1.1. call method implementation, rather than assuming that BaseCaller's call method implementation is determined by static binding.
Therefore, some things that can actually be bound statically are simply bound dynamically in consideration of security and consistency.
Optimization inspiration obtained?
Since dynamic binding needs to determine which version of the method implementation or variable to execute at runtime, it is more time-consuming than static binding.
Therefore, without affecting the overall design, we can consider modifying methods or variables with private, static or final.