An overview of the

Java as an object-oriented language, method overloading and method rewriting should be familiar.

How to determine the right target method for overloading and overwriting at the virtual machine level is discussed below:

  1. Basic definitions for overloading and overwriting
  2. Overload and static dispatch
  3. Overwrite and dynamic dispatch

Basic definitions for overloading and overwriting

The definition of overloading and overwriting is briefly restated here:

  1. Overloading refers to multiple methods with the same name but different numbers or types of arguments.
  2. Overriding means that a subclass overrides a method of the same name whose parameter signature matches the return type of the parent class.

Overload and static dispatch

Take a look at an interview question and analyze the results of the following code.

public class TestOverload { static class Cat{} static class Tom extends Cat{} static class Jack extends Cat{} public void overloadMethod(Cat cat) { System.out.println("cat method"); } public void overloadMethod(Tom tom) { System.out.println("tom method"); } public void overloadMethod(Jack jack) { System.out.println("jack method"); } public static void main(String[] args) { TestOverload testOverload = new TestOverload(); Cat tom = new Tom(); Cat jack = new Jack(); testOverload.overloadMethod(tom); testOverload.overloadMethod(jack); System.out.println("----------"); Tom tom2 = new Tom(); Jack jack2 = new Jack(); testOverload.overloadMethod(tom2); testOverload.overloadMethod(jack2); }} Output: cat method cat method ---------- Tom method Jack methodCopy the code

The above code can be a little fuzzy if you don’t understand overloading very well. Let’s look at overloaded method calls in detail.

To understand the output above we need to know the “type” of the object being created.

Cat tom = new Tom();
Copy the code

Cat Tom is declared to represent the static type Cat, followed by new Tom(), which is the actual type Tom.

When we call the overloadMethod() overloadMethod:

  1. First determine the recipient object (which is the testOverload object)
  2. Select overloaded methods. The virtual machine determines which overloaded methods to call based on the number and type of arguments. In this case, overloaded methods are selected based on static types. Since static types can be determined at compile time, overloaded methods can be determined based on static types at compile time.

About Static dispatch

Static dispatch refers to the determination of the target method to execute based on its static type. Since static typing can be determined at compile time, static dispatch occurs at compile time.

Let’s look at the bytecode from the main method of the test code above:

. Omit object creation such as bytecode 24 aload_1 25 aload_2 26 invokevirtual # 13 <. Cn/tomcoding/phrase/TestOverload overloadMethod > 29 aload_1 30 aload_3 31 invokevirtual #13 <cn/tomcoding/overload/TestOverload.overloadMethod> 34 getstatic #2 <java/lang/System.out> 37 ldc #14 <----------> 39 invokevirtual #4 <java/io/PrintStream.println> 42 new #9 <cn/tomcoding/overload/TestOverload$Tom> 45 dup 46 invokespecial #10 <cn/tomcoding/overload/TestOverload$Tom.<init>> 49 astore 4 51 new #11 <cn/tomcoding/overload/TestOverload$Jack> 54 dup 55 invokespecial #12 <cn/tomcoding/overload/TestOverload$Jack.<init>> 58 astore 5 60 aload_1 61 aload 4 63 invokevirtual #15 <cn/tomcoding/overload/TestOverload.overloadMethod> 66 aload_1 67 aload 5 69 invokevirtual #16 <cn/tomcoding/overload/TestOverload.overloadMethod> 72 returnCopy the code
  1. Bytecode indexes 26 and 31 correspond to our source code:
testOverload.overloadMethod(tom);
testOverload.overloadMethod(jack);
Copy the code

Is the same constant pool symbols referenced index # 13, corresponding method symbols refer to descriptor is (Lcn/tomcoding/phrase/TestOverload $Cat;) V.

  1. Bytecode index 34 to 39 output “———-“

  2. Bytecode indexes 42 to 61 create TOM2, Jack2 objects and store them in local traversal tables, etc.

  3. Bytecode index 63, corresponding to our source code:

testOverload.overloadMethod(tom2);
Copy the code

Call the corresponding index of constant pool symbolic reference # 15, corresponding method symbolic reference descriptor is (Lcn/tomcoding/phrase/TestOverload $Tom;) V.

  1. Bytecode index 69, corresponding to our source code:
testOverload.overloadMethod(jack2);
Copy the code

Call the corresponding index of constant pool symbolic reference # 16, corresponding method symbolic reference descriptor is (Lcn/tomcoding/phrase/TestOverload $Jack) V.

Overwrite and dynamic dispatch

Let’s start with some sample code:

public class TestOverride { static abstract class Cat{ abstract void overrideMethod(); } static class Tom extends Cat{ @Override void overrideMethod() { System.out.println("tom method"); } } static class Jack extends Cat{ @Override void overrideMethod() { System.out.println("jack method"); } } public static void main(String[] args) { Cat tom = new Tom(); Cat jack = new Jack(); tom.overrideMethod(); jack.overrideMethod(); }} Tom method Jack methodCopy the code

The output of the above code should be parsed correctly.

With this understanding of overloading and static typing, overriding methods are called with the actual type in mind.

When we use the above Tom.overrideMethod (); For example, the Tom object represents the receiver (or owner) of the overrideMethod() method.

Here’s a look at the bytecode part of the example:

 0 new #2 <cn/tomcoding/override/TestOverride$Tom>
 3 dup
 4 invokespecial #3 <cn/tomcoding/override/TestOverride$Tom.<init>>
 7 astore_1
 8 new #4 <cn/tomcoding/override/TestOverride$Jack>
11 dup
12 invokespecial #5 <cn/tomcoding/override/TestOverride$Jack.<init>>
15 astore_2
16 aload_1
17 invokevirtual #6 <cn/tomcoding/override/TestOverride$Cat.overrideMethod>
20 aload_2
21 invokevirtual #6 <cn/tomcoding/override/TestOverride$Cat.overrideMethod>
24 return

Copy the code
  1. Bytecode indexes 0 to 15, the Tom, Jack object is created and initialized, and the reference is stored in the local variable table.

  2. Bytecode indexes 17 and 21 call the overrideMethod() method.

At first glance symbol references here are all the cn/tomcoding/override/TestOverride $the overrideMethod, but invokevirtual process is as follows:

  1. Find the actual type of the first element at the top of the operand stack, called C here.
  2. If type C finds a method whose name matches the descriptor, it checks permission, passes and returns a direct reference.
  3. If type C is not found, search from bottom to top according to inheritance relationship, and verify processing in step 2 after finding.
  4. If no exception is found, an exception is thrown.

In the first step, we find that the actual type of the first element at the top of the operand stack is receiver. This determination of methods to execute at run time based on the actual type is called dynamic dispatch.

conclusion

The definitions of overloading and overwriting are not difficult to understand, but understanding how the virtual machine acknowledges the process of calling a method can lead to a deeper understanding of both. You won’t be asked a lot of interview questions.