Java_Understanding method calls

Understanding method calls

假设要针对x.f(args),其中 隐式参数 x 是一个声明为类 C 对象的隐式参数。以下是详细描述方法调用的过程:

first

The compiler checks the declared type of the object during declaration and the method name f called . Since there may be duplicate names, there may be multiple methods named f but with different parameter types. For example, there may be methods such as f(int) and f(String) at the same time. The compiler will enumerate all methods named f in class C one by one, and will also find all accessible methods named f in the superclass [ note : except for the private methods of the superclass]. This is where the compiler gets all the fallback methods that might be called. [Here, form method table 1 or a list of candidate methods, and then further screen out the method that should be called finally.

What are implicit parameters -> The implicit parameter is the object itself on which the method is called.

In Java, implicit parameters are parameters that are passed automatically during a method call without being explicitly provided in the method call. Implicit parameters are usually used by method calls on objects.
Sample code:

public class Example {
    
    
    private int value;
    public Example(int value) {
    
    
        this.value = value;
    }
    public void printValue() {
    
    
        System.out.println(value);
    }
    public static void main(String[] args) {
    
    
        Example example = new Example(10);
        example.printValue();
    }
}

In the code above, printValue()the method is an instance method, which has no explicit parameters. However, it has access to implicit parameters value, which are examplepassed through method calls on the object. In example.printValue()this line of code, exampleit's the implicit parameter.
Corresponding to implicit parameters are explicit parameters. Explicit parameters are parameters that are provided explicitly when a method is called. For example, here is an example using display parameters:

public class Example {
    
    
    public static void printValue(int value) {
    
    
        System.out.println(value);
    }
    public static void main(String[] args) {
    
    
        int value = 10;
        printValue(value);
    }
}

In the above code, printValue()the method accepts an explicit parameter value. In printValue(value)this line of code, valuethe parameters are displayed.
To sum up, implicit parameters are parameters that are automatically passed in method calls without being explicitly provided, and are usually used through object method calls. In contrast, explicit parameters are parameters that are provided explicitly when the method is called.

Next

The compiler analyzes the parameter types provided in method calls. It matches these parameter types against all candidate method parameter types, looking for a method that exactly matches the supplied parameter type. This step is called overloading resolution .

Suppose we call xf("Hello_world_work0806"), the compiler will select the f method with parameter type String instead of int.
Note that this process can get quite complicated as type conversions are allowed (for example, int can be converted to double, Manager can be converted to Employee, etc.).

[If the compiler cannot find a method that exactly matches the type of the provided parameter, or if there are multiple methods that match it after type conversion, the compiler will report an error. 】At this point, the compiler has succeededDetermine the method name and parameter types that need to be called.This means that during the method call, the compiler determines exactly which method should be called through the above steps. This process ensures the accuracy and reliability of method calls.

public class Mains {
    
    
    public static void main(String[] args) {
    
    
        printNumber(5); // 输出 "Printing int: 5"
        printNumber(3.14); // 输出 "Printing double: 3.14"
    }
    
    static void printNumber(int num) {
    
    
        System.out.println("Printing int: " + num);
    }

    static void printNumber(double num) {
    
    
        System.out.println("Printing double: " + num);
    }
}

If there are multiple methods named f whose parameter types can accept the parameters you provide, the compiler will choose the method whose parameter type is closest to the parameter type you provide. For example, if you call f(5) and there is an f method with an argument of type int and an f method with argument type double, the compiler will choose the f method with argument type int because the int type is more accurate than the double type. Close to the parameter type you provide.

This process can get complicated because Java allows type conversions. For example, int can be converted to double, Manager can be converted to Employee, etc. So, if the parameter types you provide don't exactly match the parameter types of any of the candidate methods, the compiler will try to perform type conversions to find a method that accepts the parameters you provide.

The method's name and parameter list are called the method's signature.

方法的参数列表包括参数的数量、类型和顺序。方法的签名不包括方法的返回类型和访问修饰符。

In Java, a method's signature consists of the method's name and parameter list, which uniquely identifies a method.

public void calculateSum(int a, int b)

In the above example, the name of the method is calculateSum and the parameter list is int a and int b, so the signature of the method is calculateSum(int, int).

f(int) and f(String) are two methods with the same name but different parameters, so their signatures are different.
In a subclass, if you define a method with the same signature as the superclass, the subclass method will "override" the method with the same signature in the superclass.

The return type is not part of the signature. However, when overriding a method, it is necessary to ensure the compatibility of the return type: —> allow subclasses to change the return type of the overridden method to a subtype of the original return type.

允许子类在覆盖(重写)父类方法时,将方法的返回类型修改为父类方法返回类型的子类型。
		换句话说,子类可以返回更具体的子类型,而不必仅仅返回与父类方法完全相同的类型。

In Java, a method's signature consists of the method's name and parameter list, not the method's return type. This means that if two methods have the same name and parameter list, but different return types, their method signatures are the same. In this case, the compiler cannot distinguish between the two methods.

However, when it comes to subclasses overriding (overriding) parent class methods, return type compatibility does need to be considered. Although the signatures of the methods are the same, the return type must satisfy the principle of covariance, that is, the return type of the subclass method must be a subtype of the return type of the superclass method. This is done to ensure that child class objects can be correctly treated as parent class objects and that return values ​​2 can be handled when using parent class methods . If this condition is not met, the compiler will report an error, as this may result in a type mismatch error.

For example, suppose there is a parent class A and a subclass B, which respectively define a method foo() with the same name, the return type of the parent class method is A, and the return type of the subclass method is B. The subclass B can override the method of the parent class A and change the return type to B, (1) Because B is a subclass of A, the B object can be regarded as an A object. (2) Then when we call the foo() method through the subclass object, an object of type B is returned.

子类型返回的是父类型的子类型。
	也就是说,如果在子类中重写父类的方法,并将返回类型改为父类返回类型的子类型,那么子类方法的返回值将是子类型的对象。
class A {
    
    
    public A foo() {
    
    
        return new A();
    }
}
class B extends A {
    
    
    @Override
    public B foo() {
    
    
        return new B();
    }
}

In this way, when we call the foo() method of the subclass object through the parent class reference, the subclass object B is returned. This approach is called covariance [There is an explanation later】Return type, which allows us to return more specific types in subclasses, providing better flexibility and readability.

Because B is a subclass of A, B inherits A's methods. This includes the method name and parameter list.
When you override a method of parent class A in subclass B, you can change the return type to B. This is legal because B is a subclass of A, so objects of B can be treated as objects of A.
When you create an object of subclass B and call the overridden method, you can actually treat the returned B object as an extension of the A object. This is the embodiment of polymorphism, you can call the subclass method through the parent class reference without knowing the specific subclass type.

Concrete example:
class Animal {
    
    
    public Animal reproduce() {
    
    
        return new Animal();
    }
}
class Dog extends Animal {
    
    
    @Override
    public Dog reproduce() {
    
    
        return new Dog();
    }
}

In the above code, there is a parent class Animal and a subclass Dog. The parent class Animal defines a method reproduce()with a return type of Animal. The subclass Dog overrides the method of the parent class Animal and changes the return type to Dog.
When we call the method with an object of subclass Dog reproduce():

Dog dog = new Dog(); // 创建一个新的 Dog 对象,赋值给 dog 变量
Dog newDog = dog.reproduce(); // 调用 dog 对象的 reproduce() 方法,返回一个新的 Dog 对象,赋值给 newDog 变量

By overriding parent class methods in subclasses, we can express the behavior of subclass objects more specifically. In this example, the subclass Dog overrides reproduce()the method of the parent class Animal and ensures that an object of type Dog is returned. In this way, when calling a method using a subclass Dog object reproduce(), we can directly obtain a new Dog object.
Insert image description here

detailed:

When a parent class has a method that returns a certain type, a subclass can override this method and return a subtype of that type.

Suppose there is a parent class Animaland a subclass Dog, and Animalthere is a method in the class makeSound(), the return type is String, which represents the sound made by the animal. In subclasses Dog, you can override makeSound()the method and return Barkan instance of the class that represents the dog's bark.

class Animal {
    
    
    public String makeSound() {
    
    
        return "Some generic animal sound";
    }
}

class Dog extends Animal {
    
    
    public Bark makeSound() {
    
    
        return new Bark();
    }
}

class Bark {
    
    
    public String sound() {
    
    
        return "Woof woof!";
    }
}

In this example, the subclass Dogoverrides the parent class Animal's makeSound()method, and the return type Stringchanges from to Barkan instance of class, which is a subtype of the parent method's return type. This way, you can call Dogthe class's makeSound()methods to get a more specific result rather than just a generic animal sound.

"Covariant" is a type relationship -> some properties or methods of a subtype can be more specific (specialized) than the parent type."

"Covariant" (covariant) is a type relationship that refers to the relationship between a subtype and a parent type. In this relationship, some properties or methods of the subtype can be more specific (specialized) than the parent type. . In the case you mention, "covariant return type" means that the return type of the subclass method can be a subtype of the return type of the original method. This means that subclasses can return more specific types without violating the rules for method signatures.

The covariant return type is mainly reflected in the subclass overriding the parent class method and changing the return type of the method so that it returns a more specific type than the parent class. Specifically, the following sections demonstrate the use of covariant return types:

class Publication {
    
    
    private String title;

    public Publication(String title) {
    
    
        this.title = title;
    }

    public String getTitle() {
    
    
        return title;
    }
}

class Book extends Publication {
    
    
    private String author;

    public Book(String title, String author) {
    
    
        super(title);
        this.author = author;
    }

    public String getAuthor() {
    
    
        return author;
    }
}

class Magazine extends Publication {
    
    
    private int issueNumber;

    public Magazine(String title, int issueNumber) {
    
    
        super(title);
        this.issueNumber = issueNumber;
    }

    public int getIssueNumber() {
    
    
        return issueNumber;
    }
}

class Library {
    
    
    public Publication getRecommendation() {
    
    
        // 返回一个 Publication 对象作为推荐读物
        return new Publication("Generic Recommendation");
    }
}

class BookLibrary extends Library {
    
    
    @Override
    public Book getRecommendation() {
    
    
        // 返回一个 Book 对象作为推荐读物
        return new Book("The Catcher in the Rye", "J.D. Salinger");
    }
}

class MagazineLibrary extends Library {
    
    
    @Override
    public Magazine getRecommendation() {
    
    
        // 返回一个 Magazine 对象作为推荐读物
        return new Magazine("National Geographic", 123);
    }
}

In this example, Librarythe class's getRecommendationmethod return type is Publicationand BookLibraryand MagazineLibraryrespectively override this method and change the return type to the more specific Bookand Magazine. This approach allows subclass methods to return more specific types than the parent class while still maintaining consistent method signatures.

Therefore, the covariant return type here is that the subclass method overrides the parent class method and returns a more specific type. This is in line with "the relationship between the subtype and the parent type. In this relationship, some aspects of the subtype A property or method can be more specific (specialized) than the parent type."

Covariance means that the return type of a subtype can be a subtype of the supertype, while contravariance means that the return type of a subtype can be a supertype of the supertype.

逆变表示一个泛型类型的参数类型在继承关系中变得更加具体(即变窄)。
	逆变通常在方法参数中使用,允许传递更通用的类型。
协变表示一个泛型类型的返回类型在继承关系中变得更加通用(即变宽)。
	协变通常在方法返回值中使用,允许返回更具体的类型。

Insert image description here

  1. Covariance: If type B is a subtype of type A, then in some contexts, we can use an object of type B instead of an object of type A. This usually applies to the return type of a method. For example, if a method returns an object of type Animal, it can also return an object of type Dog (assuming Dog is a subclass of Animal).

  2. Contravariance: If type B is a subtype of type A, then in some contexts, we can use an object of type A instead of an object of type B. This usually applies to the parameter types of methods. For example, if a method accepts an object of type Dog as a parameter, it can also accept an object of type Animal (assuming Dog is a subclass of Animal).

  3. The main purpose of covariance and contravariance is to provide greater flexibility and type safety. They allow us to write more general and reusable code while maintaining type safety. For example, if we have a method that handles Animal objects, through inversion, we can apply this method to Dog objects without having to write a method that specifically handles Dog objects.

class Animal {
    
    
    public void eat() {
    
    
        System.out.println("Animal eats");
    }
}

class Dog extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("Dog eats");
    }
}

class AnimalHelper {
    
    
    // 协变:返回类型是协变的
    public Dog getAnimal() {
    
    
        return new Dog();
    }

    // 逆变:参数类型是逆变的
    public void feedAnimal(Animal animal) {
    
    
        animal.eat();
    }
}

public class Mains {
    
    
    public static void main(String[] args) {
    
    
        AnimalHelper helper = new AnimalHelper();

        // 协变:我们可以将Dog赋值给Animal
        Animal animal = helper.getAnimal();

        // 逆变:我们可以将Dog传递给接受Animal的方法
        helper.feedAnimal(new Dog());
    }
}

In this example, two methods in the AnimalHelper class demonstrate the concepts of covariance and contravariance:

The getAnimal() method uses covariance: the return type Dog is covariant. This means that you can assign a method with a return type of Dog to a reference variable of type Animal. Because Dog is a subclass of Animal, this covariance operation is safe.

The feedAnimal(Animal animal) method uses contravariance: the parameter type Animal is contravariant. This means that you can pass an object of type Dog to a method that accepts a parameter of type Animal. Since Dog is a subclass of Animal, the contravariant operation is also safe.

In the main method, you show how to use these covariant and contravariant properties:

Covariance: You call the helper.getAnimal() method and assign the return value to a reference variable animal of type Animal. This is because you can assign the Dog type to the Animal type, taking advantage of the covariance of the return type.

Contravariance: You call the helper.feedAnimal(new Dog()) method, passing the Dog object to a method that accepts an Animal type parameter. This is because you can pass the Dog type to a method that accepts an Animal type parameter, taking advantage of the contravariant nature of the parameter type.
Insert image description here

Notice

Covariance:
In covariance, we can assign an instance of a derived class (like Dog) to a reference of a base class (like Animal). This is because a derived class is a special type of the base class and has all the properties and methods of the base class, but possibly additional features.
Covariance involves return types. For example, in covariance, you can assign the return value to a reference variable of type Animal if the method return type is Dog.

Contravariance
In contravariance, we can pass an instance of a base class (such as Animal) to a method that accepts a parameter of type derived class (such as Dog). This is because the base class is the common type of the derived class, and the base class object contains the common behavior of the derived class object.
Contravariance involves method parameter types. For example, in contravariance, you can pass a parameter of type Dog to a method that accepts a parameter of type Animal.

Insert image description here

Difference
In covariance, "assignment" involves assigning a reference variable of a concrete type (such as Dog) to a more general type (such as Animal). This is due to the covariant nature of the return type, which ensures that the characteristics of the derived class object are still valid in the base class reference.
In contravariance, "passing" involves passing an object of a base class type to a method that accepts a parameter of a derived class type. This is due to the contravariant nature of parameter types, allowing common properties of base class objects to be correctly handled in derived class methods.
With the code you provided, you did show the application of both scenarios. Through the getAnimal() method of the AnimalHelper class, you demonstrate covariance because you can assign an instance of type Dog to a reference variable of type Animal. With the feedAnimal(Animal animal) method, you demonstrate contravariance because you can pass an object of type Dog to a method that accepts a parameter of type Animal. Together, these two features allow you to manipulate different types of objects more flexibly in generic methods.

1. Specifically explain how to achieve _covariance

Specifically, the return type of the getAnimal method is Dog, but in the main method, we assign the Dog object to a variable of Animal type. This is because Dog is a subtype of Animal, so we can use Dog objects instead of Animal objects. This is the concept of covariance.

class AnimalHelper {
    
    
    // 协变:返回类型是协变的
    public Dog getAnimal() {
    
    
        return new Dog();
    }
}

public class Contravariance {
    
    
    public static void main(String[] args) {
    
    
        AnimalHelper helper = new AnimalHelper();

        // 协变:我们可以将Dog赋值给Animal
        Animal animal = helper.getAnimal();
        animal.eat();
    }
}

In this code, the Dog object is assigned to a variable of type Animal, which is an example of covariance.

2. Detailed description of how to achieve _inversion

In the feedAnimal method of the AnimalHelper class, the parameter type is Animal. This means that you can pass any Animal object or object of its subclass to this method. Therefore, you can pass a Dog object to this method even though it expects an Animal object. This is the concept of inversion.

In the main method, you create a Dog object and pass it to the feedAnimal method. Although the feedAnimal method expects an Animal object, since Dog is a subclass of Animal, you can pass it a Dog object. This is a specific application of inversion.

The result of running this code will be the output "Dog eats", because the feedAnimal method calls the eat method of the incoming object, and the incoming object is a Dog object, so the eat method overridden in the Dog class is called.

Static Binding and Dynamic Binding

In general, static binding determines the method call at compile time, which is fast but not flexible enough, while dynamic binding determines the method call based on the actual object type at runtime, which is more flexible but relatively slow. In Java, most method calls use dynamic binding because it supports features such as polymorphism and inheritance.

1. Static Binding : Methods or functions determined during compilation [shareable]

静态绑定是在**编译时期确定方法或函数的调用**,不需要等到运行时期才决定。
	它适用于private方法、static方法、final方法和构造器的调用。

Static binding (static binding) means that the method or function call can be determined at compile time (compile time), without waiting until runtime to decide. In static binding, the invocation of a method or function is determined based on the reference type (also known as the compile-time type). When the compiler compiles the code, it will determine the method or function defined by the type based on the reference type. Therefore, statically bound method or function calls are determined at compile time and will not be affected by the actual type of the object at runtime.

  • Occurs at compile time.
  • Applicable to private, static, final methods and constructors.
  • The method to call can be determined at compile time because the method selection is based on the declared type of the reference variable.
  • The compiler can parse method calls directly during the compilation phase, so it is faster.

Static binding is suitable for the following situations:

  1. Private methods : Since private methods are not inheritable within the same class, the compiler can directly determine the method to be called.

Private methods cannot be overridden by subclasses, so when calling a private method, the compiler knows exactly which method should be called.

  1. Static method : Static methods belong to classes rather than instances, so they can be called without instantiating the object. The compiler can determine which static method to call based on the declared class.

The static method belongs to the class rather than the object, so when the static method is called, the compiler can know exactly which method should be called.

  1. Final method : The final modified method cannot be overridden by subclasses, so the compiler can accurately know that the final method in the declared class is to be called.

The final method cannot be overridden by subclasses, so when the final method is called, the compiler can know exactly which method should be called.

  1. Constructors : When creating an object, the compiler accurately selects the appropriate constructor based on the constructor's argument list.

Constructors are special methods used to create objects. When a constructor is called, the compiler knows exactly which constructor should be called.

One characteristic of static binding is that it can parse method calls at compile time, so it executes faster. However, static binding lacks the features of dynamic inheritance and polymorphism because it does not take into account the actual type of the object. In contrast, dynamic binding determines method calls based on the actual object type at runtime, providing greater flexibility and polymorphism.

Example code:

class Animal {
    
    
    public static void printType() {
    
    
        System.out.println("This is an animal.");
    }
}

class Dog extends Animal {
    
    
    public static void printType() {
    
    
        System.out.println("This is a dog.");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Animal animal = new Animal();
        Animal dog = new Dog();
        Dog realUnderDogClassDog = new Dog();
        animal.printType(); // 静态绑定,输出:"This is an animal."
        dog.printType(); // 静态绑定,输出:"This is an animal.",因为静态方法不会被子类重写
        realUnderDogClassDog.printType();// 静态绑定,输出:"This is a dog."
    }
}

Since the printType() method is a static method, the invocation of the static method is determined according to the reference type, and will not be affected by the actual type of the object at runtime. So whether the animal variable refers to the Animal object or the dog variable refers to the Dog object, when the printType() method is called, the printType() method defined in the Animal class will be called directly. Therefore, the output is "This is an animal.".
This is the characteristic of static binding. The method call has been determined at compile time and will not be determined according to the actual type of the object.

Static constants and static binding are two different concepts.

Static constants refer to constants that are determined at compile time and cannot be modified. It is usually finaldeclared using keywords and must be initialized when declared. Static constants are initialized when the class is loaded and can be accessed directly through the class name. Static constants are class-level, meaning that they are shared throughout the class, and all objects share the same static constant value.
Static binding means that the call to a method or function is determined at compile time. It occurs when accessing static methods, static variables, static constants, and accessing static members using class names. Static binding determines the method or function call based on the reference type and will not be affected by the actual type of the object. This is because static members and static methods are associated with the class and do not depend on the creation of the object. Therefore, no matter which object reference is used to call a static member or static method, the static member or static method defined in the class will be called.
To sum up, static constants are constants that are determined at compile time and cannot be modified, while static binding is a call to a method or function that is determined at compile time. Static constants are at the class level and can be directly accessed through the class name, while static binding determines the method or function call based on the reference type and is not affected by the actual type of the object.

In Java, staticand finalare keywords used to define the characteristics of variables. They have different meanings and uses.

First, let's take a look at the meanings of static, finaland respectively static final:

  1. static: It is used to indicate that the variable or method is at the class level, does not depend on the creation of the object, and can be directly accessed through the class name. However, staticit does not mean that it cannot be modified. Static variables can be modified during the running of the program.

  2. final: Used to represent constants, which cannot be modified once assigned. Can be used in variables, methods, classes, etc.

  3. static final: Combines staticand finaltogether to represent a class-level unmodifiable constant.

Now, let's apply the above concepts to the code and think about the output step by step:

public class StaticFinalExample {
    
    
    public static String staticVariable = "Static Variable";
    public final String finalVariable = "Final Variable";
    public static final String staticFinalVariable = "Static Final Variable";

    public static void main(String[] args) {
    
    
        StaticFinalExample obj1 = new StaticFinalExample();

        // 输出1:访问静态变量
        System.out.println("Static Variable (obj1): " + obj1.staticVariable);
        obj1.staticVariable = "hello";  // 修改静态变量的值
        System.out.println("Static Variable (obj1): " + obj1.staticVariable);

        // 输出2:访问 final 变量
        System.out.println("Final Variable (obj1): " + obj1.finalVariable);
        // 尝试修改 final 变量的值不会导致编译错误,但运行时会报错
        // obj1.finalVariable = "Modified Final Variable"; 

        // 输出3:访问 static final 变量
        System.out.println("Static Final Variable (obj1): " + obj1.staticFinalVariable);
        // 尝试修改 static final 变量的值会导致编译错误
        // obj1.staticFinalVariable = "Modified Static Final Variable"; 

        // 输出4:输出修改后的值
        System.out.println("Static Variable (obj1): " + obj1.staticVariable);
    }
}

Note that attempting to modify the values ​​of the finaland static finalvariables will result in a compile error because they are declared as unmodifiable constants.

Insert image description here

Summarize:
  • staticRepresents class-level variables or methods that can be accessed directly through the class name.
  • finalRepresents a constant, which cannot be modified once assigned.
  • static finalRepresents an unmodifiable constant at the class level.
  • staticand finalcan be used independently or together.Insert image description here
- Definition in Math class:
 /**
     * The {@code double} value that is closer than any other to
     * <i>e</i>, the base of the natural logarithms.
     */
    public static final double E = 2.7182818284590452354;

    /**
     * The {@code double} value that is closer than any other to
     * <i>pi</i>, the ratio of the circumference of a circle to its
     * diameter.
     */
    public static final double PI = 3.14159265358979323846;

    /**
     * Constant by which to multiply an angular value in degrees to obtain an
     * angular value in radians.
     */
    private static final double DEGREES_TO_RADIANS = 0.017453292519943295;

    /**
     * Constant by which to multiply an angular value in radians to obtain an
     * angular value in degrees.
     */
    private static final double RADIANS_TO_DEGREES = 57.29577951308232;

Insert image description here

2.Dynamic Binding :

动态绑定(Dynamic Binding)是一种方法调用的绑定方式,它发生在运行时(runtime)。在动态绑定中,方法的选择是基于对象的实际类型,而不仅仅是引用变量的声明类型。
	它适用于非私有、非静态、非final的实例方法和接口方法的调用,实现了多态性。

Dynamic binding refers to determining the call of a method or function based on the actual type of the object during runtime. In dynamic binding, the invocation of a method or function is determined based on the actual type (also known as the runtime type). When a program calls a method or function at run time, the actual type of the object is used to determine which method or function is called. Therefore, dynamically bound method or function calls are determined at runtime based on the actual type of the object.

  • Occurs at runtime.
  • Suitable for situations where the method call depends on the actual type of the implicit parameter.
  • The method to be called is determined at runtime based on the actual type of the object. Method selection is based on the actual type of the object.
  • Requires an actual lookup on each method call, so is relatively slow, but provides greater flexibility and polymorphism.

Dynamic binding is suitable for the following situations:

  1. Calling non-private, non-static, and non-final instance methods: These methods can be overridden by subclasses, so when calling these methods, which method to call will be determined according to the actual type of the object.
  2. Call the interface method: The interface method is an abstract method definition, and the implementation class needs to implement the interface method. Therefore, when calling the interface method, it will determine which method to call according to the actual type of the implementation class.

Code example:

动态绑定是指在运行时期根据对象的实际类型来确定方法或函数的调用。
class Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("狗发出汪汪声");
    }
}

class Cat extends Animal {
    
    
    public void makeSound() {
    
    
        System.out.println("猫发出喵喵声");
    }
}

public class DynamicBindingExample {
    
    
    public static void main(String[] args) {
    
    
        Animal animal1 = new Animal();
        Animal animal2 = new Dog();
        Animal animal3 = new Cat();

        animal1.makeSound(); // 输出:"动物发出声音"
        animal2.makeSound(); // 输出:"狗发出汪汪声"
        animal3.makeSound(); // 输出:"猫发出喵喵声"
    }
}

We define Animalthe class using and its two subclasses Dogand Cat.
AnimalThere is a makeSound()method in the class, and subclasses Dogand Catsubclasses respectively override this method and provide their own implementations.
In mainthe method, we create an Animalobject animal1, and two subclass objects animal2and animal3.

当我们调用 `makeSound()` 方法时,
	由于动态绑定的作用,实际调用的方法是根据对象的实际类型来确定的。因此,

animal1.makeSound()What is called is the method Animalof class makeSound().
animal2.makeSound()What is called is the method Dogof class makeSound().
animal3.makeSound()What is called is the method Catof class makeSound().
This is the feature of dynamic binding, which allows the program to determine the method to call based on the actual type of the object at runtime, rather than based on the reference type.

Summarize:

The dynamic binding feature allows subclass objects to call the appropriate method at runtime based on the actual type, rather than based on the compile-time type. This makes the code more adaptable. When a new subclass is introduced, the new subclass-specific methods can be called without modifying the existing code.

To ensure that when a superclass method is overridden (overridden) in a subclass, the access rights of the subclass method cannot be lower than the access rights of the superclass method. If the superclass method is public, then the subclass method must also be declared public. If this rule is violated, the compiler will report an error, because this may cause the inherited method to be unable to be accessed normally in some cases.


  1. To_1: The Java Virtual Machine (JVM) is responsible for parsing and executing Java programs at runtime [ generated and maintained based on compiled bytecode files. 】. When a Java program starts, the JVM loads the bytecode file and stores the method information of the class in the method area (Method Area). Including creating a method table [also known as a virtual method table __vtable].
    The method table is used to store the instance method information and calling address of the class, so as to perform dynamic binding and polymorphic calling at runtime. Therefore, forming the method table is done by the JVM while the program is running. [The structure and storage method of the method table are defined by the JVM, so different JVM implementations may have some differences in details.
    To_2: The method table (method table) is a data structure that the virtual machine pre-calculates for each class [storage method information], which is used to record the signatures of all methods in the class and the actual methods to be called [it contains all the methods defined in the class Information, including method name, parameter type, return type, access modifier, etc. 】.
    When the program runs and calls a method using dynamic binding, the virtual machine looks for the corresponding method in the method table based on the actual type of the object. If a method corresponding to the actual type is found, the method is called; if not found, it is searched in the parent class.Until the corresponding method is found or the top-level parent class is reached.[The main function of the method table is to support dynamic binding and polymorphism at runtime.
    To_3: When the program is running, when a method is called, the JVM will determine the specific method to be called based on the information in the method table. Because the method table records the actual address or offset of the method, polymorphism can be realized, that is, which specific method to call is determined according to the actual type of the object at runtime.
    It should be noted that the method table is generated at compile time, not dynamically generated at run time . Therefore, for dynamically added methods or methods generated through the reflection mechanism, the method table will not contain information about these methods. ↩︎

  2. The return value type of a Java class can be any valid data type, including primitive data types (such as int, double.char, etc.), object types (such as custom classes, String, etc.), array types, etc. The specific return value type depends on the definition and implementation of the method. ↩︎

Guess you like

Origin blog.csdn.net/m0_74154295/article/details/132143979