1. Understand

Polymorphism. The word is made up of poly+morphism. Poly means many and morphism means states.

Polymorphism literally means multiple forms of an object.

Phenomenon of 2.

Real things often reflect a variety of forms, such as students, students are a kind of people, then a specific classmate Zhang SAN is both a student and a person, that is, there are two forms.

Using Java code, it can be expressed as:

Person zhangsan = new Student();
Copy the code

3. The premise

  1. There must be subclasses and superclasses with inheritance or implementation.
  2. A subclass redefines methods in its parent class and calls its methods when those methods are called.
  3. A reference to a parent class points to an object of a subclass.

4. The advantages and disadvantages

Advantages: Can make the program has a good extension, and can handle objects of all classes in common.

Disadvantages: Methods unique to subclasses cannot be accessed using polymorphism.

Characteristics of 5.

5.1 Upward Transformation

5.1.1 definition

A parent type reference to an object of a child type is an automatic conversion.

5.1.2 case

// Dog descends from Animal
Animal animal = new Dog();
Copy the code

In an upward transition, a parent reference can only call the method originally defined by the parent or the method overridden by the subclass, while a method unique to the subclass cannot be called.

5.2 Dynamic Binding

5.2.1 to introduce

See the following code:

/ / parent class
public class Animal {

    public void whoop(a) {
        System.out.println("I am an animal"); }}/ / subclass Dog
public class Dog extends Animal {

    @Override
    public void whoop(a) {
        System.out.println("I am a dog"); }}/ / subclass Cat
public class Cat extends Animal {

    @Override
    public void whoop(a) {
        System.out.println("I am a cat"); }}/ / test class
public class Test {

    public static void whoopTest(Animal animal) {
        animal.whoop();
    }

    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        // Executes the whoop method of the Dog class
        whoopTest(dog);
        // Execute the whoop method of the Cat classwhoopTest(cat); }}Copy the code

Whooop (whoopTest) : whooop (whoopTest) : whooop (whoopTest) : whooop (whoopTest) But why does the runtime automatically call the Whoop method defined in the Dog class when the real type of the argument I pass in is Dog? What if the runtime automatically calls the whoop method defined in the Cat class when THE real type of the argument I pass in is Cat? Dynamic binding.

5.2.2 definition

Dynamic binding refers to determining the actual type of the referenced object at run time (not compile time) and calling its corresponding method based on its actual type.

6. Transition downward

6.1 define

Definition: a subtype reference refers to an object of the subtype to which the parent type reference refers. This is a cast.

6.2 case

If a parent class reference wants to call a method unique to the child class, it can only be called through a downward transition.

Animal animal = new Dog();
// Downward transition
Dog dog = (Dog) animal;
// Dog class unique method
dog.whoop();
Copy the code

Note: The downcast parent type reference must actually refer to the same object as the target subclass, otherwise a ClassCastException will be reported.

So it’s not safe to cast down because we sometimes can’t be sure that the type of the object to which the parent type reference actually refers is the same as that of the target subclass. So we need to use the instanceof keyword.

In the example above, since we know what type the parent reference refers to as the target child, we can cast down without error.

6.3 Instanceof keyword

Use instanceof when you do not know the type of the target subclass to which the parent reference refers

// Determine if the animal reference refers to an object of type Dog
if  (animal instanceof Dog) {
    // Downward transition
    Dog dog = (Dog) animal;
    // Dog class unique method
    dog.test();
}
Copy the code

7. Static binding

7.1 define

A mechanism that determines where a method is in memory at compile time is called a static binding mechanism.

Static methods, final methods, and private methods all follow the static binding mechanism.

7.2 case

See the following code:

public class Util {

    public static int add(int a, int b) {
        returna + b; }}public class Test {

    public static void main(String[] args) {
        int result = Util.add(3.2); }}Copy the code

When the main method is compiled, the compiler passes information about the util.add method (package name, class name, method name, return value type, etc.) into a constant table (suppose a constant table) in the constant pool of the Test class. When the JVM runs the method for the first time, a constant pool resolution is performed to find the method’s direct address in the method area using information stored by the compiler, which is then stored in the A-table. When the JVM runs the method a second or more times, it can call the method bytecode directly from the direct address stored in the A-constant table.

8. Method rewrite (supplement)

Method override has the following provisions:

  • The method name, parameter list, and return type of the overridden method in a subclass must be the same as that of the overridden method in the parent class.
  • An overridden method in a subclass cannot throw an exception more broadly than the overridden method in its parent class.
  • Permission modifiers for overriding methods in subclasses cannot have lower permissions than those for overriding methods in the parent class.

In polymorphism, it is precisely because the return type of the overridden method can also be transformed upward that a rule is added:

  • The return value type of an overridden method in a subclass may be a subclass of the return value type of the overridden method in the parent class.

9. Underlying principle of polymorphism

9.1 Java Method Invocation

Java method calls have two types, dynamic method calls and static method calls.

  • Static method invocation: a case in which the specific method is called is determined at compile time.
  • Dynamic method invocation: it is at the time of invocation that the specific method is determined. This is dynamic binding, which is also the core problem to be solved by polymorphism.

There are four JVM method invocation instructions, which are Invokestatic, Invokespecial, Invokevirtual and InvokeInterface. The first two are static and the last two are dynamic.

9.2 Two Types of Dynamic Invocation

Java’s implementation of dynamic binding for method calls relies heavily on method tables. However, there are two types of invocation: class reference invokevirtual and interface reference invokeInterface. The two implementations are different.

In terms of performance, class reference calls perform better. Invokevirtual looks up methods based on offsets, whereas InvokeInterface looks up methods based on searches.

Class reference calls can be dynamically bound by changing only the pointer to the method table (methods with the same signature have the same index number in the method table of the parent and subclass).

Interface reference calls require scanning the entire method table for dynamic binding (because one class may implement multiple interfaces and another class may implement only one interface and not have the same index number).

9.3 Class Reference Invocation

The general process of class reference invocation is as follows:

During compilation, the Java compiler compiles the source code for each class into individual class bytecodes, writing symbolic references to the methods that that class calls into the corresponding class file. During execution, the JVM finds the symbolic reference to the calling method based on the class file, then the offset on the class’s method table, and finally the absolute address of the method based on the this pointer. If the method is found in the method table, it is called directly. If it does not, the JVM assumes that the parent class has not been overridden and searches the method table from parent class to subclass by inheritance.

JVM structure diagram:

Can be seen from the above, in program running period, when you need to use a class, class loading subsystem to the corresponding class file is loaded into the JVM, and internally to establish the type of the class information (type information is the JVM store a data structure of a class file) that contains the class definition of all the information (method code, static variables, the member variables, Method table). All type information is stored in the method area.

Note:

  1. The type information in the method area is different from the class object in the heap. There is only one instance of type information in a method area, and there can be more than one class object in the heap. The corresponding type information in the method area can be accessed through the class object in the heap. Similar to Java’s reflection mechanism, all information about a class can be accessed through a class object.

  2. The method area, like the heap space, is an area of memory shared by individual threads. Used to store information about classes that have been loaded by the virtual machine, constants, static variables, compiler compiled code, etc.

  3. The runtime constant pool is part of the method area, and in addition to class information, the class file has a constant pool of information. Used to hold the various symbol references generated by the compiler, which go into the runtime constant pool of the method when the class is loaded.

  4. The purpose of the method area’s memory reclamation is to recycle constant pools and unload type information.

Case study:

/ / parent class
public class Animal {

    public void whoop(a) {
        System.out.println("I am an animal");
    }

    public void speak(a) {
        System.out.println("Animal is speaking");
    }

    @Override
    public String toString(a) {
        return "This is an animal"; }}/ / subclass
public class Dog extends Animal {

    // Custom methods
    public void eat(a) {
        System.out.println("Dog is eating");
    }
    
    // Override the parent method
    @Override
    public void speak(a) {
        System.out.println("Dog is speaking");
    }

    // Override the Object method
    @Override
    public String toString(a) {
        return "This is a dog"; }}/ / subclass
public class Cat extends Animal {

    // Custom methods
    public void sing(a) {
        System.out.println("Cat is singing");
    }

    // Override the parent method
    @Override
    public void speak(a) {
        System.out.println("Cat is speaking");
    }

    // Override the Object method
    @Override
    public String toString(a) {
        return "This is a cat"; }}Copy the code

When the class files for the three classes are loaded into the JVM, the JVM method area contains information about each class.

Method area occupation is shown in the figure below:

In the figure above, you can clearly see the pointer to the calling method, and you can see that methods with the same signature have the same offset in the method table. This offset simply means that the offsets of Dog methods derived from Object and Animal are the same as the offsets of the same Animal methods. It has nothing to do with Cat.

The Dog and Cat method tables include methods derived from Object, methods derived from Animal, and custom methods. Note the specific method address that the method entry points to. For example, among the methods inherited from Object in the Dog method table, only the toString() method points to its own implementation (the method code of Dog), and the rest points to the method code of Object. The whoop() method points to its parent’s implementation (Animal’s method code). The speak method points to its implementation, and the eat() method points to its implementation.

This explains why, in polymorphism, if a subclass overrides a parent class’s method, the subclass’s method is really called. If the subclass does not override the parent class’s method, the parent class’s method is actually called.

10. Extension

See the following code:

/ / parent class
public class Animal {

    protected String name = "animal";
}

/ / subclass
public class Dog extends Animal {

    protected String name = "dog";
}

/ / subclass
public class Cat extends Animal {

    protected String name = "cat";
}

public class Test {

    public static void main(String[] args) {
        Animal dog = new Dog();
        // animal
        System.out.println(dog.name);
        Animal cat = new Cat();
        // animalSystem.out.println(cat.name); }}Copy the code

Why does a parent reference output a member variable of the parent class, no matter who the real child object refers to?

A: In Java, only methods are overridden, so member variables are not polymorphic. Although dog refers to the dog instance, dog is of the Animal type, so the name of dog instance represents the characteristics of Animal, so the output is “Animal”, the same as cat.