当前位置:网站首页>Implementation principle of inheritance, encapsulation and polymorphism

Implementation principle of inheritance, encapsulation and polymorphism

2022-07-03 21:24:00 Laughing Gao

Welcome to Java The inheritance of learning 、 encapsulation 、 The realization principle of polymorphism

Catalog


from JVM Structure begins to talk about polymorphism

Java The implementation of dynamic binding for method calls mainly depends on the method table , But the implementation of calling by class reference and calling by interface reference is different . Overall speaking , When a method is called ,JVM First, find the corresponding constant pool , Get the symbolic reference of the method , And look up the method table of the calling class to determine the direct reference of the method , Finally, the method is actually called . The following is a detailed introduction of the relevant parts involved in the process .

JVM Structure

Typical Java The runtime structure of the virtual machine is shown in the figure below

chart 1.JVM Runtime structure

 chart 1.JVM Runtime structure

In this structure , We only discuss the method area which is closely related to this paper (method area). When the program needs the definition of a class , Load subsystem (class loader subsystem) Load required class file , And create the type information of this class internally , This type of information is stored in the method area . Type information generally includes the method code of this class 、 Class variables 、 Definition of member variables, etc . so to speak , Type information is class Java Internal structure of files at run time , Contains all the changes in the Java Information defined in the file .

be aware , This type of information and class The object is different .class The object is JVM After loading a class in the heap (heap) Objects created in on behalf of this class , You can use this class Object to access this type of information . For example, the most typical application , stay Java Application in reflection class Object to access all methods supported by this class , Defined member variables, etc . As you can imagine ,JVM In type information and class Objects maintain their references to each other in order to access each other . The relationship between the two can be compared to the relationship between the process object and the real process .

Java Method call mode of

Java There are two types of method calls for , Dynamic method call and static method call . Static method call refers to the static method call method of the class , Is statically bound ; And dynamic method calls need to have the object that the method calls , Is dynamically bound . Class call (invokestatic) It is the case that the specific call method has been determined at compile time , And the instance calls (invokevirtual) The specific calling method is determined at the time of calling , This is dynamic binding , It is also the core problem to be solved by polymorphism .

JVM There are four method call instructions for , Namely invokestatic,invokespecial,invokesvirtual and invokeinterface. The first two are static bindings , The last two are dynamically bound . This article can also be said to be for JVM Investigation on the implementation of the latter two calls .

Constant pool (constant pool)

The constant pool holds a Java Some constant information of class reference , Contains some string constants and symbolic reference information for classes .Java The constant pool in the class file generated by code compilation is a static constant pool , When the class is loaded inside the virtual machine , The constant pool that generates classes in memory is called the runtime constant pool .

The constant pool can be logically divided into multiple tables , Each table contains a class of constant information , This paper only discusses the Java Call related constant pool table .

CONSTANT_Utf8_info

String constant table , This table contains all string constants used by this class , For example, string reference in code 、 Referenced class name 、 The name of the method 、 String descriptions of other referenced classes and methods, etc . Any constant strings involved in the rest of the constant pool table are indexed to that table .

CONSTANT_Class_info

Class information table , Contains a symbolic reference to any referenced class or interface , Each entry mainly contains an index , Point to CONSTANT_Utf8_info surface , Indicates the fully qualified name of the class or interface .

CONSTANT_NameAndType_info

Name type table , Index containing the name and descriptor information of any method or field referenced in the string constant table .

CONSTANT_InterfaceMethodref_info

Interface method reference table , Contains the description of any interface method referenced , Mainly including class information index and name type index .

CONSTANT_Methodref_info

Class method reference table , Contains description information for any type of method referenced , Mainly including class information index and name type index .

chart 2. Relationship between tables in constant pool

You can see , Given the index of any method , After the corresponding entry is found in the constant pool , We can get the class index of this method (class_index) And name type index (name_and_type_index), Then the type information, name and descriptor information of the method are obtained ( Parameters , Return value, etc ). Notice that all constant strings are stored in the CONSTANT_Utf8_info For other table indexes .

Method table and method call

Method table is the core of dynamic call , It's also Java The main way to realize dynamic call . The type information stored in the method area , Contains all methods defined by this type and pointers to these method codes , Note that these specific method codes may be overridden methods , It can also be a method inherited from the base class .

If there is a class definition Person, Girl, Boy,

detailed list 1
 class Person {
public String toString(){
return "I'm a person.";
}
public void eat(){}
public void speak(){}

}

class Boy extends Person{
public String toString(){
return "I'm a boy";
}
public void speak(){}
public void fight(){}
}

class Girl extends Person{
public String toString(){
return "I'm a girl";
}
public void speak(){}
public void sing(){}
}

When these three classes are loaded into the Java After virtual machine , The method area contains information about each class .Girl and Boy The method table in the method area can be represented as follows :

chart 3.Boy and Girl Method table

 chart 3.Boy and Girl Method table

You can see ,Girl and Boy Method table of contains inherited from Object Methods , Inherit from direct parent Person Methods and their newly defined methods . Note the specific method address that the method table entry points to , Such as Girl Inherited from Object The methods of , Only toString() Point to your own implementation (Girl Method code of ), The rest points to Object Method code of ; It is inherited from Person Methods eat() and speak() Point to respectively Person Method implementation and its own implementation .

Person or Object Any method of , In their method tables and their subclasses Girl and Boy Location in the method table of (index) It's the same . such JVM To call instance methods, you only need to specify the first few methods in the call method table .

Call as follows :

detailed list 2

 class Party{
…
void happyHour(){
Person girl = new Girl();
girl.speak();
…
}
}

Compile Party Class time , Generate  girl.speak() The method call for is assumed to be :

Invokevirtual #12

Set the calling code to girl.speak(); #12 yes Party Index of constant pool of class .JVM The procedure to execute the call instruction is as follows :

chart 4. Resolve call procedure

 chart 4. Resolve call procedure

JVM First of all to see Party Constant pool index of is 12 The entry of ( Should be CONSTANT_Methodref_info type , A symbolic reference that can be seen as a method call ), See more about constant pools (CONSTANT_Class_info,CONSTANT_NameAndType_info ,CONSTANT_Utf8_info) It can be concluded that the method to be called is Person Of speak Method ( Note references girl Is its base class Person type ), see Person Method table , obtain speak Method offset in the method table 15(offset), This is the direct reference of the method call .

After resolving the direct reference of the method call ( Method table offset 15),JVM Perform real method calls : Parameters called by instance method this Get specific objects ( namely girl Object in the heap pointed to ), According to this, the corresponding method table of the object is obtained (Girl Method table ), Then call the method pointed to by an offset in the method table (Girl Of speak() Method implementation ).

Interface call

because Java Class can implement multiple interfaces at the same time , When a method is called with an interface reference , The situation is different .Java Allow a class to implement multiple interfaces , In a sense, it's equivalent to multiple inheritance , In this way, the position of the same method in the method table of the base class and the derived class may be different .

detailed list 3

interface IDance{
void dance();
}

class Person {
public String toString(){
return "I'm a person.";
}
public void eat(){}
public void speak(){}

}

class Dancer extends Person
implements IDance {
public String toString(){
return "I'm a dancer.";
}
public void dance(){}
}

class Snake implements IDance{
public String toString(){
return "A snake.";
}
public void dance(){
//snake dance
}
}
chart 5.Dancer Method table ( Check out the big picture

 chart 5.Dancer Method table

You can see , Due to interface intervention , Inherited from interface IDance Methods dance() In the class Dancer and Snake The position in the method table of , Obviously we can't call it correctly by giving the offset of the method table Dancer and Snake This method of . This is also Java The calling interface method has its own calling instruction. (invokeinterface) Why .

Java The interface method is called by searching the method table , Call the following methods

invokeinterface #13

JVM First look at the constant pool , Identify symbolic references for method calls ( name 、 Return value and so on ), And then use it this Get the method table of the instance pointed to , Then search the method table to find the appropriate method address .

Because every interface call searches for a method table , So in terms of efficiency , Interface method calls are always slower than class method calls .

The results are as follows :
 Picture description here
You can see System.out.println(dancer);  It's called Person Of toString Method .

The principle of inheritance

Java Inheritance mechanism is a technology of reusing classes , In principle , It's better to use combination technology , So understand inheritance , First of all, we need to understand how class composition technology can realize class reuse .

Using composite technology to reuse classes
Suppose that the requirement now is to create a basic type ,String Type and an object of another non basic type . How to deal with it ?

For variables of basic type , Directly define the member variables in the new class , But for non basic type variables , Not only do you need to declare its references in a class , You also need to initialize this object manually .

What needs to be noted here is , The compiler does not create objects by default for all references , Because in many cases, this will increase the unnecessary burden , therefore , Initialize the right object at the right time , Initialization can be done through the following locations :

Where objects are defined , Prior to construction method execution .
In the constructor .
Just before using , This is called lazy initialization .
Use instance initialization .

class Soap {
    private String s;
    Soap() {
        System.out.println("Soap()");
        s = "Constructed";
    }
    public String tiString(){
        return s;
    }
}

public class Bath {
    // s1  Initialize before constructor 
    private String s1 = "Happy", s2 = "Happy", s3, s4;
    private Soap soap;
    private int i;
    private float f;
    
    public Both() {
        System.out.println("inSide Both");
        s3 = "Joy";
        f = 3.14f;
        soap = new Soap();
    }
    
    {
        i = 88;
    }
    
    public String toString() {
        if(s4 == null){
            s4 = "Joy"
        }
        return "s1 = " + s1 +"\n" +
               "s2 = " + s2 +"\n" +
               "s3 = " + s3 +"\n" +
               "s4 = " + s4 +"\n" +
               "i = " + i +"\n" +
               "f = " + f +"\n" +
               "soap = " + soap;
    }
}

Inherit
Java Inheritance in by extend Keyword implementation , The grammar of combination is relatively plain , And inheritance is a special grammar . When a class inherits from another class , Then this class can have the domain and method of another class .

class Cleanser{
    private String s = "Cleanser";
    
    public void append(String a){
        s += a;
    }
    public void apply(){
        append("apply");
    }
    public void scrub(){
        append("scrub");
    }
    public String toString(){
        return s;
    }
    public static void main(String args){
        Cleanser c = new Cleanser();
        
        c.apply();
        System.out.println(c);
    }
}

public class Deter extends Cleanser{
    public void apply(){
        append("Deter.apply");
        super.scrub();
    }
    public void foam(){
        append("foam");
    }
    public static void main(String args){
        Deter d = new Deter();
        
        d.apply();
        d.scrub();
        d.foam();
        System.out.println(d);
        Cleanser.main(args);
    }
}

In the above code , Shows some features of inheritance syntax :

Subclass can directly use the common methods and member variables in the parent class ( Usually to protect the data domain , Member variables are private )
Methods in the parent class can be overridden in the child class , That is, the subclass overrides the method of the parent class , At this time, if you need to call the method of the overridden parent class , You need to use super To specify that the method in the parent class is called .
Methods not in the parent class can be customized in the child class .
You can find that both of the above classes main Method , Which class is called in the command line main Method , for example :java Deter.
Principles of inheritance grammar
Next, we will create subclass objects to analyze what inheritance syntax does in places we can't see .

Think about it first , How to understand objects created with Subclasses , First, this object contains all the information of the subclass , But it also contains all the public information of the parent class .

Let's look at a piece of code , Observe the initialization of subclasses when creating objects , Will parent related methods be used .

class Art{
    Art() {
        System.out.println("Art Construct");
    }
}

class Drawing extends Art {
    Drawing() {
        System.out.println("Drawing Construct");
    }
}

public class Cartoon extends Drawing {
    public Cartoon() {
        System.out.println("Cartoon construct");
    }
    public void static main(String args) {
        Cartoon c = new Cartoon();
    }
}
/*output:
Art Construct
Drawing Construct
Cartoon construct
*/

You can see by looking at the code , In instantiation Cartoon when , It's actually instantiating from the top-level parent class down one by one , That is to say, three objects are finally instantiated . By default, the compiler will add code calling the default constructor of the parent class to the constructor of the child class .

therefore , Inheritance can be understood as compiler helps us to complete the special composition technology of class , That is, there is an object of the parent class in the child class , It enables us to call the method of the parent class with the subclass object . In the eyes of developers, it's just a keyword .

Be careful : Although inheritance is very close to combinatorial technology , But inheritance has other features that are more different from composition , For example, the objects of the parent class are invisible to us , For the method in the parent class, the corresponding permission verification is also done .

that , If the construction method in a class is parameterized , How to operate ?( Use super Keyword display call )

See the code :

class Game {
    Game(int i){
        System.out.println("Game Construct");
    }
}

class BoardGame extends Game {
    BoardGame(int j){
        super(j);
        System.out.println("BoardGame Construct");
    }
}
public class Chess extends BoardGame{
    Chess(){
        super(99);
        System.out.println("Chess construct");
    }
    public static void main(String args) {
        Chess c = new Chess();
    }
}
/*output:
Game Construct
BoardGame Construct
Chess construc
*/

Implementation principle of overloading and rewriting

Just beginning to learn Java When , I understand Java This is an interesting feature : rewrite and heavy load . At first, sometimes it's easy to confuse names . I believe I know Java Students of this language should understand these two characteristics , Maybe it's just from the language level , however jvm How to achieve them ?

Official introduction of heavy load :

One .  overload:
The Java programming language supports overloading methods, and Java can distinguish between methods with different method signatures. This means that methods within a class can have the same name if they have different parameter lists .

Overloaded methods are differentiated by the number and the type of the arguments passed into the method.

You cannot declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart.

The compiler does not consider return type when differentiating methods, so you cannot declare two methods with the same signature even if they have a different return type.

First look at a piece of code , Let's see the execution result of the code :

public class OverrideTest {
 
    class Father{}
 
    class Sun extends Father {}
 
    public void doSomething(Father father){
        System.out.println("Father do something");
    }
 
    public void doSomething(Sun father){
        System.out.println("Sun do something");
    }
 
    public static void main(String [] args){
        OverrideTest overrideTest = new OverrideTest();
        Father sun = overrideTest.new Sun();
        Father father = overrideTest.new Father();
        overrideTest.doSomething(father);
        overrideTest.doSomething(sun);
    }
}

Take a look at the execution result of this code , Will print at the end :

Father do something
Father do something

Why do you print such a result ? Two concepts are introduced first : Static dispatch and dynamic dispatch

Static Dispatch : A dispatch action that relies on a static type to locate the execution version of a method is called a static dispatch

Dynamic dispatch : The runtime determines the method execution version dispatch process according to the actual type .

The difference between them is :

1.  Static dispatch occurs at compile time , Dynamic dispatch occurs during operation ;

2.  private,static,final Method occurs at compile time , And can't be rewritten , Once rewriting occurs , Will be processed during operation .

3.  Overload is static dispatch , Rewrite is dynamic dispatch

Back to the question above , Because overloading occurs at compile time , So it has been determined twice during compilation doSomething The parameters of the method are Father type , stay class The file already points to Father Symbol reference of class , So it will print twice at the end Father do something.

Two . override:
An instance method in a subclass with the same signature (name, plus the number and the type of its parameters) and return type as an instance method in the superclass overrides the superclass's method.

The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method that it overrides. An overriding method can also return a subtype of the type returned by the overridden method. This subtype is called a covariant return type.

Or the code above , Slightly changed

public class OverrideTest {
 
    class Father{}
 
    class Sun extends Father {}
 
    public void doSomething(){
        System.out.println("Father do something");
    }
 
    public void doSomething(){
        System.out.println("Sun do something");
    }
 
    public static void main(String [] args){
        OverrideTest overrideTest = new OverrideTest();
        Father sun = overrideTest.new Sun();
        Father father = overrideTest.new Father();
        overrideTest.doSomething();
        overrideTest.doSomething();
    }
}


Will print at the end :

Father do something

Sun do something

I'm sure everyone will know the result , So this result jvm How to achieve it ?

At compile time , Only the call will be recognized Father Class doSomething Method , The actual type of the object will not be found until the runtime .

First, the implementation of this method ,jvm Would call invokevirtual Instructions , The instruction looks for the actual type of the object that the first element at the top of the stack points to , If there is a method called for this type , Then the verification process will be followed , Otherwise, continue to find its parent class . That's why subclasses can directly call methods that their parents have access to . In short , The actual type of the object will be determined at runtime , Determine the method execution version based on this actual type , This process is called dynamic dispatch .override Implementation dependency of jvm Dynamic dispatch of .

Reference article

https://blog.csdn.net/dj_dengjian/article/details/80811348
https://blog.csdn.net/chenssy/article/details/12757911
https://blog.csdn.net/fan2012huan/article/details/51007517
https://blog.csdn.net/fan2012huan/article/details/50999777
https://www.cnblogs.com/serendipity-fly/p/9469289.html
https://blog.csdn.net/m0_37264516/article/details/86709537

原网站

版权声明
本文为[Laughing Gao]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202142320427151.html