Home  >  Article  >  Java  >  Detailed introduction to static binding and dynamic binding in Java

Detailed introduction to static binding and dynamic binding in Java

高洛峰
高洛峰Original
2016-12-26 16:17:231264browse

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, dynamic binding occurs at runtime
2. Use variables or methods modified with private or static or final, use static Binding. Virtual methods (methods that can be overridden by subclasses) will be dynamically bound according to 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 method.

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

22:19 $ java TestMain
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 String. Object of type as 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.

Verification

Just looking at the appearance cannot prove that static binding is performed. You can verify it by using javap to compile it.

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
}

saw this line 18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V is indeed statically bound, and it is confirmed that the call receives the String object as Parameter caller method.

Example of overriding method

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

22:27 $ java TestMain
a String instance in SubCaller

The above code has an implementation of the call method in Caller. SubCaller inherits Caller and rewrites it. 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.

Verification

Dynamic binding cannot be directly verified using javap. If it is proved that static binding is not performed, it means that dynamic binding is performed.

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 above result, 18: invokevirtual #6 // Method TestMain$Caller.call:(Ljava/lang/String;)V Here is TestMain$Caller.call instead of TestMain$SubCaller.call, Because the compile time cannot determine whether to call the implementation of the subclass or the parent class, it can only be left to the dynamic binding at runtime.

When overloading meets overriding

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 rewrites this Two methods. In fact, this situation is a compound situation of the above two situations.

The following code will first be statically bound to determine the call method whose parameter is a String object, and then dynamically bound at runtime to determine whether to execute the call implementation of the subclass or the parent class.

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

22:30 $ java TestMain
a String instance in in SubCaller

Verification

Since it has been introduced above, I will only post the decompilation result here

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 have dynamic binding?

In fact, theoretically, the binding of certain methods can also be achieved by static binding. For example:

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.

Suppose BaseCaller and SuperCaller in a certain framework 1.0

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");
  }
}

and we use framework 1.0 to implement this. Caller inherits from BaseCaller and calls the super.call method.

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 again assume 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 in version 1.1 super. The call should be implemented using the call method of SuperCall, rather than the call method of BaseCaller determined by static binding.

So, some things that can actually be bound statically are simply bound dynamically in consideration of security and consistency.

Optimization inspiration obtained?

Because 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.

So without affecting the overall design, we can consider modifying methods or variables with private, static or final.

For more detailed introduction to static binding and dynamic binding in Java, please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn