Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the dynamic binding mechanism of Java

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

Today, the editor will share with you the relevant knowledge about what the Java dynamic binding mechanism is. The content is detailed and the logic is clear. I believe most people still know too much about this knowledge, so share this article for your reference. I hope you can get something after reading this article. Let's take a look at it.

Static binding mechanism

/ / called class package hr.test; class Father {public static void F1 () {System.out.println ("Father- F1 ()");}} / / call static method import hr.test.Father; public class StaticCall {public static void main () {Father.f1 (); / / call static method}}

The statement (Father.f1 ()) that executes the method call in the above source code is compiled into an instruction by the compiler: invokestatic # 13. Let's see how JVM handles this instruction.

(1) # 13 in the instruction refers to the index entry of the 13th constant table in the constant pool of class StaticCall (for more information on the constant pool, see "Class File contents and constant Pool"). This constant table (CONSTATN_Methodref_info) records symbolic references to method F1 information (including the class name, method name, and return type in which F1 is located). JVM will first find the fully qualified name of the class where method F1 is located based on this symbolic reference: hr.test.Father

(2) immediately after JVM loads, links, and initializes the Father class

(3) then find the direct address of the F1 () method in the method area of the Father class, and record this direct address in the constant pool index 13 of the StaticCall class. This process is called constant pool parsing, and when Father.f1 () is called again later, the bytecode of the F1 method will be found directly.

(4) after parsing the constant table of the constant pool index item 13 of class StaticCall, JVM can call the F1 () method and begin to interpret and execute the instructions in the F1 () method.

Through the above procedure, we found that after constant pool parsing, JVM can determine exactly where the F1 () method to be called is located in memory. In fact, this information is recorded in the constant pool of the StaticCall class at compile time. This way of determining which method to call during the compilation phase is called the static binding mechanism.

Except for static methods modified by static, all private methods modified by private and methods modified by final that prohibit subclass overrides are compiled into invokestatic instructions. In addition, all class initialization methods and will be compiled into invokespecial instructions. JVM will use the static binding mechanism to call these methods smoothly.

Dynamic binding mechanism

Package hr.test; / / called parent class class Father {public void F1 () {System.out.println ("father-f1 ()");} public void F1 (int I) {System.out.println ("father-f1 () para-int" + I);}} / / called subclass class Son extends Father {public void F1 () {/ / overrides the method System.out.println ("Son-f1 ()") of the parent class } public void F1 (char c) {System.out.println ("Son-s1 () para-char" + c);}} / / call method import hr.test.*; public class AutoCall {public static void main (String [] args) {Father father=new Son (); / / polymorphic father.f1 (); / / print result: Son-f1 ()}}

There are three important concepts in the above source code: polymorphism, method overriding, and method overloading. The printed results are clear to everyone, but how does JVM know that f.f1 () calls methods in the subclass Sun instead of methods in Father? Before we explain this problem, let's briefly talk about a very important data structure managed by JVM-the method table.

When the class is loaded by JVM, a lot of information is stored for the class in the method area (see "Java Virtual Machine Architecture" for details). There is a data structure called the method table. It records the direct memory address of the visible method bytecode of the current class and all its superclasses in the form of an array. The following figure shows the method table of the Father and Sun classes in the method area in the source code above:

The method table in the figure above has two characteristics: (1) the method table of the subclass inherits the methods of the parent class, such as Father extends Object. (2) the same method (same method signature: method name and parameter list) has the same index in the method table of all classes. For example, F1 () in the Father method table and F1 () in the Son method table are in item 11 of their respective method tables.

For the above source code, the compiler first compiles the main method into the following bytecode instruction:

0 new hr.test.Son [13] / / opens up the memory space of a Son object in the heap and pushes the object reference into the Operand stack

3 dup

4 invokespecial # 7 [15] / / call the initialization method to initialize Son objects in the heap

7 astore_1 / / the Son object reference of the pop-up Operand stack is pressed into the local variable 1

8 aload_1 / / take out the object reference in local variable 1 and push it into the Operand stack

9 invokevirtual # 15 / / call F1 () method

12 return

The detailed calling procedure of the invokevirtual instruction is as follows:

(1) # 15 in the invokevirtual instruction refers to the index entry of the 15th constant table in the constant pool of class AutoCall. This constant table (CONSTATN_Methodref_info) records symbolic references to method F1 information (including the class name, method name, and return type in which F1 is located). JVM will first find the fully qualified name of the class that calls method F1 based on this symbolic reference: hr.test.Father. This is because the object father of the class that calls method F1 is declared to be of type Father.

(2) look for method F1 in the method table of type Father, and if found, record the index item 11 of method F1 in the method table (as shown in the figure above) to the 15th constant table in the constant pool of class AutoCall (constant pool parsing). One thing to note here: if there is no method F1 in the Father type method table, then even if there is a method table in the Son type, it will not pass at compile time. Because the object father of the class that calls method F1 is declared to be of type Father.

(3) before invoking the invokevirtual instruction, there is an aload_1 instruction that pushes a reference to the Son object that started to be created in the heap into the Operand stack. Then the invokevirtual instruction first finds the Son object in the heap based on the reference to the Son object, and then further finds the method table of the type to which the Son object belongs. The process is shown in the following figure:

(4) this is the index item 11 of the method table in the # 15 constant table, which is parsed in step (2). You can locate the method F1 () in the method table of type Son, and then find the memory space where the method bytecode is located through the direct address.

Obviously, the location of the calling method F1 cannot be determined based on the declared type (Father) of the object (father), and the location of the F1 method must be determined based on the object type Son that father actually created in the heap. In the process of running the program, we call it the dynamic binding mechanism to locate the method through the method table of the dynamically created object.

The above procedure clearly reflects how JVM locates the exact method in the case of polymorphic calls covered by the method. But how is the following calling method JVM located? (still use the Father and Son types in the above code)

Public class AutoCall {public static void main (String [] args) {Father father=new Son (); char caterpillars; father.f1 (c); / / print result: father-f1 () para-int 97}}

The problem is that there is no method with F1 (char) signature in the Fahter type. But the print results show that JVM calls the F1 (int) method in the Father type, not the F1 (char) method in the Son type.

According to the call procedure described in detail above, it is first clear that JVM first parses the constant pool based on the type Father declared by the object father (that is, using the index entry in the Father method table instead of the symbol reference in the constant pool). If there is no "appropriate" method in Father, constant pool parsing cannot be done, which will not pass during the compilation phase.

So what is the "appropriate" approach? Of course, a method with exactly the same method signature is naturally appropriate. But what if the parameter type in the method cannot be found in the declared type? For example, when father.f1 (char) is called in the above code, the Father type does not have the method signature of F1 (char). In fact, JVM will find a way to "make do" by automatically transforming the parameters to find the "right" method. For example, char can be automatically converted to int, so this method can be matched in the Father class (for the automatic transformation of Java, see "transition between Java types"). But there is another problem: what if there are two ways to "make do" through automatic transformation? For example, the following code:

Class Father {public void F1 (Object o) {System.out.println ("Object");} public void F1 (double [] d) {System.out.println ("double []");}} public class Demo {public static void main (String [] args) {new Father () .f1 (null); / / print result: double []}}

Null can be referenced to any reference type, so how does JVM determine the "appropriate" method. A very important criterion is that if one method can accept any parameters passed to another method, then * methods are relatively inappropriate. For example, the above code: any parameter passed to the F1 (double []) method can be passed to the F1 (Object) method, and vice versa, then the F1 (double []) method is more appropriate. So JVM calls this more appropriate method.

Summary

(1) all private methods, static methods, constructors and initialization methods use static binding mechanism. The symbolic reference of the calling method in the constant pool has been indicated in the compiler phase, and the JVM runtime only needs to do a constant pool parsing.

(2) the call of class object method must adopt dynamic binding mechanism in the process of running.

First, find the "appropriate" method based on the declared type of the object (the type referenced by the object). The specific steps are as follows:

① this method is most appropriate if you can match a method with exactly the same method signature (the same parameter type) in the declaration type.

② looks for ways to "make do" in cases where Article ① is not satisfied. The standard is to match the parameter types by automatically transforming them. If you match to multiple automatically transformed method signatures f (A) and f (B), the following criteria are used to determine the appropriate method: all the parameters passed to the f (A) method can be passed to f (B), then f (A) is the most appropriate. On the contrary, f (B) is the most suitable.

If ③ still cannot find an "appropriate" method in the declared type, the compile phase will fail.

Then, the corresponding method table is found according to the actual type of object created in the heap, from which the location of the specific method in memory is determined.

Overwrite (override)

An instance method can override (override) all instance methods with the same signature that are accessible in its superclass, thus enabling dynamic dispatch (dynamic dispatch); in other words, VM will select the override method to call based on the runtime type of the instance. Overwriting is the foundation of object-oriented programming technology and is a form of name reuse that is not generally discouraged:

Class Base {public void f () {}} class Derived extends Base {public void f () {}}

Hidden (hide)

A domain, static method, or member type can hide all fields, static methods, or member types accessible in its superclass with the same name (the same method signature for the method). Hiding a member will prevent it from being inherited.

Class Base {public static void f () {}} class Derived extends Base {private static void f () {} / / hides Base. F ()}

Overload (overload)

Methods in one class can overload another method, as long as they have the same name and different signatures. The overloaded method specified by the call is selected at compile time.

Class CircuitBreaker {public void f (int I) {} / / int overloading public void f (String s) {} / / String overloading}

Shading (shadow)

A variable, method, or type can shadow all variables, methods, or types with the same name within a closed text range. If an entity is obscured, you cannot refer to it with its simple name; depending on the entity, sometimes you cannot refer to it at all.

Class WhoKnows {static String sentence= "I don't know."; public static void main (String [] args) {String sentence= "I don't know."; / / shadows static field System.out. Println (sentence); / / prints local variable}}

Although masking is usually discouraged, there is a general idiom that does involve masking. The constructor often reuses a domain name from its class as a parameter to pass the value of the named domain. This idiom is not risk-free, but most Java programmers agree that the benefits of this style outweigh the benefits

The risks are:

Class Belt {private find int size; / / Parameter shadows Belt. Size public Belt (int size) {this. Size=size;}}

Masking (obscure)

A variable can mask a type with the same name, as long as they are all in the same scope: if the name is used in the scope where both the variable and the type are allowed, it will be referenced to the variable. Similarly, a variable or type can mask a package. Masking is a form of name reuse in which two names are in different namespaces, including variables, packages, methods, or types. If a type or package is masked, you cannot refer to it by its simple name, except in a context where syntax allows only one name in its namespace. Following naming habits can greatly eliminate the possibility of masking:

Public class Obscure {static String System;// Obscures type java.lang.System public static void main (String [] args) / / Next line won't compile:System refers to static field System. Out. Println ("hello, obscure world!");}} above is all the content of the article "what is the dynamic binding mechanism of Java?" Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report