Llamadas al método Java_Understanding

Comprender las llamadas a métodos

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

primero

El compilador verifica el tipo declarado del objeto durante la declaración y el nombre del método f llamado . Dado que puede haber nombres duplicados, puede haber varios métodos denominados f pero con diferentes tipos de parámetros. Por ejemplo, puede haber métodos f(int) y f(String). El compilador enumerará todos los métodos denominados f en la clase C uno por uno, y también encontrará todos los métodos accesibles denominados f en la superclase [ Nota : excepto los métodos privados de la superclase]. Esta es la forma en que el compilador obtiene todos los métodos alternativos que podrían llamarse. [Aquí, se forma la tabla de métodos 1 o una lista de métodos candidatos, y luego se filtran aún más los métodos que finalmente deberían llamarse.

¿Qué son los parámetros implícitos? -> El parámetro implícito es el objeto mismo en el que se llama al método.

En Java, los parámetros implícitos se refieren a parámetros que se pasan automáticamente durante una llamada a un método sin tener que proporcionarse explícitamente en la llamada al método. Los parámetros implícitos generalmente se utilizan mediante llamadas a métodos en objetos.
Código de muestra:

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();
    }
}

En el código anterior, printValue()el método es un método de instancia, que no tiene parámetros explícitos. Sin embargo, tiene acceso a parámetros implícitos value, que se examplepasan a través de llamadas a métodos en el objeto. En example.printValue()esta línea de código, examplees el parámetro implícito.
Los parámetros implícitos corresponden a parámetros explícitos. Los parámetros explícitos son parámetros que se proporcionan explícitamente cuando se llama a un método. Por ejemplo, aquí hay un ejemplo que usa parámetros de visualización:

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

En el código anterior, printValue()el método acepta un parámetro explícito value. En printValue(value)esta línea de código, valuese muestran los parámetros.
En resumen, los parámetros implícitos son parámetros que se pasan automáticamente en llamadas a métodos sin proporcionarse explícitamente y, por lo general, se utilizan a través de llamadas a métodos de objetos. Por el contrario, los parámetros explícitos son parámetros que se proporcionan explícitamente cuando se llama al método.

Próximo

El compilador analiza los tipos de parámetros proporcionados en las llamadas a métodos. Comparará estos tipos de parámetros con todos los tipos de parámetros de métodos candidatos, buscando un método que coincida exactamente con el tipo de parámetro proporcionado. Este paso se llama resolución de sobrecarga .

Supongamos que llamamos a xf ("Hello_world_work0806"), el compilador seleccionará el método f con el tipo de parámetro String en lugar de int.
Tenga en cuenta que este proceso puede volverse bastante complicado debido a las conversiones de tipos permitidas (por ejemplo, int se puede convertir en doble, Manager se puede convertir en Empleado, etc.).

[Si el compilador no puede encontrar un método que coincida exactamente con el tipo de parámetro proporcionado, o si más de un método coincide después de la conversión de tipo, el compilador informará un error. 】 En este punto, el compilador ha tenido éxito.Determine el nombre del método y los tipos de parámetros que deben llamarseEsto significa que en el proceso de llamada al método, el compilador determina con precisión el método que se debe llamar mediante los pasos anteriores. Este proceso garantiza la precisión y confiabilidad de las llamadas a métodos.

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);
    }
}

Si hay varios métodos llamados f cuyos tipos de parámetros pueden aceptar los parámetros que usted proporciona, el compilador elegirá el método cuyo tipo de parámetro sea más cercano al tipo de parámetro que usted proporcione. Por ejemplo, si llama a f(5) y hay un método f con un tipo de parámetro int y un método f con un tipo de parámetro double, entonces el compilador elegirá el método f con un tipo de parámetro int, porque el tipo int es más conveniente que el tipo doble. Cerca del tipo de parámetro que proporciona.

Este proceso puede complicarse porque Java permite conversiones de tipos. Por ejemplo, int se puede convertir en doble, Manager se puede convertir en Empleado, etc. Por lo tanto, si el tipo de parámetro que proporciona no coincide exactamente con el tipo de parámetro de cualquier método candidato, el compilador intentará realizar una conversión de tipo para encontrar un método que pueda aceptar el parámetro que proporcionó.

El nombre del método y la lista de parámetros se denominan firma del método.

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

En Java, la firma de un método consta del nombre del método y la lista de parámetros, que identifica de forma única un método.

public void calculateSum(int a, int b)

En el ejemplo anterior, el nombre del método es calcularSum y la lista de parámetros es int a e int b, por lo que la firma del método es calcularSum(int, int).

f(int) y f(String) son dos métodos con el mismo nombre pero parámetros diferentes, por lo que sus firmas son diferentes.
En una subclase, si define un método con la misma firma que la superclase, entonces este método de subclase "anulará" el método con la misma firma en la superclase.

El tipo de devolución no forma parte de la firma. Sin embargo, al anular un método, debe garantizar la compatibilidad del tipo de retorno: -> Permitir que las subclases cambien el tipo de retorno del método anulado a un subtipo del tipo de retorno original.

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

En Java, la firma de un método consta del nombre del método y la lista de parámetros, no del tipo de retorno del método. Esto significa que si dos métodos tienen el mismo nombre y lista de parámetros pero diferentes tipos de retorno, sus firmas de método son las mismas. En este caso, el compilador no puede distinguir entre los dos métodos.

Sin embargo, es necesario considerar la compatibilidad del tipo de retorno cuando se trata de subclases que anulan (anulan) los métodos de la clase principal. Aunque la firma del método es la misma, el tipo de retorno debe satisfacer el principio de covarianza, es decir, el tipo de retorno del método de la subclase debe ser un subtipo del tipo de retorno del método de la clase principal. Esto se hace para garantizar que los objetos de subclase puedan tratarse adecuadamente como objetos de superclase y puedan manejar valores de retorno cuando se utilizan métodos de superclase . Si no se cumple esta condición, el compilador informará un error, ya que esto puede provocar un error de discrepancia de tipos.

Por ejemplo, supongamos que hay una clase principal A y una subclase B. Definen respectivamente un método foo() con el mismo nombre. El tipo de retorno del método de la clase principal es A y el tipo de retorno del método de la subclase es B. La subclase B puede anular el método de la clase principal A y cambiar el tipo de retorno a B. (1) Debido a que B es una subclase de A, el objeto B puede considerarse como un objeto A. (2) Luego, cuando llamamos al método foo() a través de un objeto de subclase, se devuelve un objeto de tipo B.

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

De esta manera, cuando llamamos al método foo () del objeto de subclase a través de la referencia de la clase principal, se devuelve el objeto de subclase B. Este enfoque se llama covarianza [Hay una explicación más adelante.】Tipo de retorno, que nos permite devolver tipos más específicos en subclases, proporcionando mayor flexibilidad y legibilidad.

Como B es una subclase de A, B hereda los métodos de A. Esto incluye el nombre del método y la lista de parámetros.
Cuando anula un método de la clase principal A en la subclase B, puede cambiar el tipo de retorno a B. Esto es legal porque B es una subclase de A, por lo que los objetos de B pueden tratarse como objetos de A.
Cuando crea un objeto de la subclase B y llama al método anulado, en realidad puede tratar el objeto B devuelto como una extensión del objeto A. Esta es una manifestación del polimorfismo: puede llamar a métodos de subclase a través de referencias de clase principal sin conocer el tipo de subclase específico.

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

En el código anterior, hay una clase principal Animal y una subclase Perro. La clase principal Animal define un método reproduce()con un tipo de retorno de Animal. La subclase Dog anula el método de la clase principal Animal y cambia el tipo de retorno a Dog.
Cuando llamamos a un método usando un objeto de la subclase Perro reproduce():

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

Al anular los métodos de la clase principal en las subclases, podemos expresar el comportamiento de los objetos de la subclase de manera más específica. En este ejemplo, la subclase Perro anula reproduce()el método de la clase principal Animal y garantiza que se devuelva un objeto de tipo Perro. De esta manera, al llamar a un método usando un objeto Dog de subclase reproduce(), podemos obtener directamente un nuevo objeto Dog.
Insertar descripción de la imagen aquí

detallado:

Cuando una clase principal tiene un método que devuelve un determinado tipo, una subclase puede anular este método y devolver un subtipo de ese tipo.

Supongamos que hay una clase principal Animaly una subclase Dog, y hay Animalun método en la clase makeSound()con un tipo de retorno Stringque representa el sonido emitido por el animal. En una subclase Dog, puede anular makeSound()el método y devolver Barkuna instancia de la clase que representa el ladrido del perro.

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!";
    }
}

En este ejemplo, la subclase Doganula el método Animalde la clase principal makeSound()y el tipo de retorno Stringcambia de a Barkuna instancia de clase, que es un subtipo del tipo de retorno del método principal. De esta manera, puedes llamar Doga los métodos de la clase makeSound()para obtener un resultado más específico en lugar de simplemente un sonido animal genérico.

"Covariante" es una relación de tipo -> algunas propiedades o métodos de un subtipo pueden ser más específicos (especializados) que el tipo principal".

"Covariante" (covariante) es una relación de tipo que se refiere a la relación entre un subtipo y un tipo padre. En esta relación, algunas propiedades o métodos del subtipo pueden ser más específicos (especializados) que el tipo padre. En el caso que menciona, "tipos de retorno covariantes" significa que el tipo de retorno de un método de subclase puede ser un subtipo del tipo de retorno del método original. Esto significa que las subclases pueden devolver tipos más específicos sin violar las reglas de firmas de métodos.

El tipo de retorno covariante se refleja principalmente en que la subclase anula el método de la clase principal y cambia el tipo de retorno del método para que devuelva un tipo más específico que la clase principal. Específicamente, las siguientes secciones demuestran el uso de tipos de retorno covariantes:

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);
    }
}

En este ejemplo, Libraryel getRecommendationtipo de retorno del método de clase es Publicationy, BookLibraryrespectivamente MagazineLibrary, anula este método y cambia el tipo de retorno al más específico Booky Magazine. Este enfoque permite que los métodos de subclase devuelvan tipos más específicos que la clase principal y al mismo tiempo mantengan firmas de métodos consistentes.

Por lo tanto, el tipo de retorno covariante aquí es que el método de la subclase anula el método de la clase principal y devuelve un tipo más específico. Esto está en línea con "la relación entre el subtipo y el tipo principal. En esta relación, algunos aspectos del subtipo A La propiedad o método puede ser más específico (especializado) que el tipo principal".

Covarianza significa que el tipo de retorno de un subtipo puede ser un subtipo del tipo principal, mientras que contravarianza significa que el tipo de retorno de un subtipo puede ser el supertipo del tipo principal.

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

Insertar descripción de la imagen aquí

  1. Covarianza: si el tipo B es un subtipo del tipo A, entonces, en algunos contextos, podemos sustituir objetos de tipo B por objetos de tipo A. Esto suele aplicarse al tipo de retorno de un método. Por ejemplo, si un método devuelve un objeto de tipo Animal, también puede devolver un objeto de tipo Perro (suponiendo que Perro es una subclase de Animal).

  2. Contravarianza: si el tipo B es un subtipo del tipo A, entonces, en algunos contextos, podemos usar un objeto de tipo A en lugar de un objeto de tipo B. Esto generalmente se aplica a los tipos de parámetros de los métodos. Por ejemplo, si un método acepta un objeto de tipo Perro como parámetro, también puede aceptar un objeto de tipo Animal (suponiendo que Perro es una subclase de Animal).

  3. El objetivo principal de la covarianza y la contravarianza es proporcionar mayor flexibilidad y seguridad de tipos. Nos permiten escribir código más general y reutilizable manteniendo la seguridad de tipos. Por ejemplo, si tenemos un método que trata con objetos Animal, con contravarianza podemos aplicar este método a objetos Perro sin escribir un método que trate específicamente con objetos Perro.

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());
    }
}

En este ejemplo, dos métodos de la clase AnimalHelper demuestran los conceptos de covarianza y contravarianza:

El método getAnimal() usa covarianza: el tipo de retorno Perro es covariante. Esto significa que puede asignar un método con un tipo de retorno de Perro a una variable de referencia de tipo Animal. Como Perro es una subclase de Animal, esta operación covariante es segura.

El método feedAnimal(Animal animal) utiliza contravarianza: el tipo de argumento Animal es contravariante. Esto significa que puedes pasar un objeto de tipo Perro a un método que toma un parámetro de tipo Animal. Dado que Dog es una subclase de Animal, la contravarianza también es segura.

En el método principal, muestra cómo utilizar estas propiedades covariantes y contravariantes:

Covariante: llama al método helper.getAnimal() y asigna el valor de retorno a una variable de referencia animal de tipo Animal. Esto se debe a que puedes asignar un tipo Perro a un tipo Animal, aprovechando la naturaleza covariante del tipo de retorno.

Contravarianza: llama al método helper.feedAnimal(new Dog()) y pasa el objeto Dog a un método que acepta un parámetro de tipo Animal. Esto se debe a que puede pasar el tipo Perro a un método que acepte un parámetro de tipo Animal, aprovechando la naturaleza contravariante del tipo de parámetro.
Insertar descripción de la imagen aquí

Aviso

Covarianza:
en covarianza, podemos asignar una instancia de una clase derivada (como Perro) a una referencia de una clase base (como Animal). Esto se debe a que una clase derivada es un tipo especial de la clase base y tiene todas las propiedades y métodos de la clase base, pero posiblemente características adicionales.
La covarianza implica tipos de retorno. Por ejemplo, en covarianza, puede asignar el valor de retorno a una variable de referencia de tipo Animal si el tipo de retorno del método es Perro.

Contravarianza
En contravarianza, podemos pasar una instancia de una clase base (como Animal) a un método que acepte un parámetro de tipo de clase derivada (como Perro). Esto se debe a que la clase base es el tipo común de la clase derivada y el objeto de la clase base contiene el comportamiento común del objeto de la clase derivada.
La contravarianza implica tipos de parámetros de método. Por ejemplo, en contravarianza, puede pasar un parámetro de tipo Perro a un método que acepte un parámetro de tipo Animal.

Insertar descripción de la imagen aquí

Diferencia
En covarianza, "asignación" implica asignar una variable de referencia de un tipo concreto (como Perro) a un tipo más general (como Animal). Esto se debe a la naturaleza covariante del tipo de retorno, que garantiza que las características del objeto de clase derivado sigan siendo válidas en la referencia de clase base.
En contravariación, "pasar" implica pasar un objeto de un tipo de clase base a un método que acepta un parámetro de un tipo de clase derivada. Esto se debe a la naturaleza contravariante de los tipos de parámetros, lo que permite que las propiedades comunes de los objetos de la clase base se manejen correctamente en los métodos de la clase derivada.
Con el código que proporcionaste, mostraste la aplicación de ambos escenarios. A través del método getAnimal() de la clase AnimalHelper, demuestras la covarianza porque puedes asignar una instancia de tipo Dog a una variable de referencia de tipo Animal. Con el método feedAnimal(Animal animal), demuestras la contravarianza porque puedes pasar un objeto de tipo Perro a un método que acepta un parámetro de tipo Animal. Juntas, estas dos características le permiten manipular diferentes tipos de objetos de manera más flexible en métodos genéricos.

1. Explique en detalle cómo lograr_covarianza.

Específicamente, el tipo de retorno del método getAnimal es Perro, pero en el método principal asignamos el objeto Perro a una variable de tipo Animal. Esto se debe a que Perro es un subtipo de Animal, por lo que podemos usar objetos Perro en lugar de objetos Animal. Este es el concepto de covarianza.

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();
    }
}

En este código, el objeto Perro se asigna a una variable de tipo Animal, que es un ejemplo de covarianza.

2. Explique en detalle cómo lograr_inversión.

En el método feedAnimal de la clase AnimalHelper, el tipo de parámetro es Animal. Esto significa que puedes pasar cualquier objeto Animal u objeto de su subclase a este método. Por lo tanto, puede pasar un objeto Perro a este método aunque espere un objeto Animal. Este es el concepto de inversión.

En el método principal, crea un objeto Dog y lo pasa al método feedAnimal. Aunque el método feedAnimal espera un objeto Animal, dado que Dog es una subclase de Animal, puedes pasarle un objeto Dog. Esta es una aplicación específica de la inversión.

El resultado de ejecutar este código será el resultado "Dog eats", porque el método feedAnimal llama al método eat del objeto entrante, y el objeto entrante es un objeto Dog, por lo que se llama al método eat anulado en la clase Dog.

Enlace estático y enlace dinámico

En general, el enlace estático determina la llamada al método en tiempo de compilación, que es rápido pero no lo suficientemente flexible, mientras que el enlace dinámico determina la llamada al método en función del tipo de objeto real en tiempo de ejecución, que es más flexible pero relativamente lento. En Java, la mayoría de las llamadas a métodos utilizan enlace dinámico porque admite características como el polimorfismo y la herencia.

1. Enlace estático : métodos o funciones determinados durante la compilación [compartible]

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

El enlace estático (enlace estático) significa que la llamada al método o función se puede determinar en el momento de la compilación (tiempo de compilación), sin esperar hasta el tiempo de ejecución para decidir. En el enlace estático, la invocación de un método o función se determina en función del tipo de referencia (también conocido como tipo en tiempo de compilación). Cuando el compilador compila el código, determinará el método o función definida por el tipo según el tipo de referencia. Por lo tanto, las llamadas a métodos o funciones vinculadas estáticamente se determinan en tiempo de compilación y no se verán afectadas por el tipo real del objeto en tiempo de ejecución.

  • Ocurre en tiempo de compilación.
  • Aplicable a métodos y constructores privados, estáticos y finales.
  • El método a llamar se puede determinar en tiempo de compilación porque la selección del método se basa en el tipo declarado de la variable de referencia.
  • El compilador puede resolver llamadas a métodos directamente durante la compilación, por lo que es más rápido.

La encuadernación estática es adecuada para las siguientes situaciones:

  1. Métodos privados : dado que los métodos privados no se pueden heredar dentro de la misma clase, el compilador puede determinar directamente a qué método llamar.

Los métodos privados no pueden ser anulados por subclases, por lo que cuando se llama a un método privado, el compilador puede saber exactamente qué método debe llamarse.

  1. Método estático : los métodos estáticos pertenecen a clases en lugar de instancias, por lo que se pueden llamar sin crear una instancia del objeto. El compilador puede determinar qué método estático llamar según la clase declarada.

Los métodos estáticos pertenecen a clases y no a objetos, por lo que al llamar a un método estático, el compilador puede saber exactamente qué método debe llamarse.

  1. Método final : los métodos finales modificados no pueden ser anulados por subclases, por lo que el compilador puede saber exactamente cuál llamar es el método final en la clase declarada.

El método final no puede ser anulado por subclases, por lo que cuando se llama al método final, el compilador puede saber exactamente qué método debe llamarse.

  1. Constructor : al crear un objeto, el compilador selecciona con precisión el constructor apropiado según la lista de parámetros del constructor.

Los constructores son métodos especiales que se utilizan para crear objetos. Cuando se llama a un constructor, el compilador sabe exactamente qué constructor debe llamarse.

Una característica del enlace estático es que puede analizar llamadas a métodos en tiempo de compilación, por lo que se ejecuta más rápido. Sin embargo, el enlace estático carece de las características de herencia dinámica y polimorfismo porque no tiene en cuenta el tipo real del objeto. Por el contrario, el enlace dinámico determina las llamadas a métodos en función del tipo de objeto real en tiempo de ejecución, lo que proporciona mayor flexibilidad y polimorfismo.

Código de ejemplo:

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."
    }
}

Dado que el método printType () es un método estático, la invocación del método estático se determina de acuerdo con el tipo de referencia y no se verá afectada por el tipo real del objeto en tiempo de ejecución. Entonces, ya sea que la variable animal se refiera al objeto Animal o la variable perro se refiera al objeto Perro, cuando se llama al método printType (), se llamará directamente al método printType () definido en la clase Animal. Por lo tanto, los resultados de salida son todos "Esto es un animal".
Esta es la característica del enlace estático: la llamada al método se determina en el momento de la compilación y no se determinará en función del tipo real del objeto.

Las constantes estáticas y el enlace estático son dos conceptos diferentes.

Las constantes estáticas se refieren a constantes que se determinan en el momento de la compilación y no se pueden modificar. Por lo general, se finaldeclara mediante palabras clave y debe inicializarse cuando se declara. Las constantes estáticas se inicializan cuando se carga la clase y se puede acceder a ellas directamente por el nombre de la clase. Una constante estática es de nivel de clase, lo que significa que se comparte en toda la clase y todos los objetos comparten el mismo valor de constante estática.
El enlace estático se refiere a determinar el método o la llamada a la función en el momento de la compilación. Ocurre al acceder a métodos estáticos, variables estáticas, constantes estáticas y acceder a miembros estáticos mediante nombres de clases. El enlace estático determina la llamada al método o función en función del tipo de referencia y no se verá afectado por el tipo real del objeto. Esto se debe a que los miembros estáticos y los métodos estáticos están asociados con la clase y no dependen de la creación del objeto. Por lo tanto, no importa qué referencia de objeto se utilice para llamar a un miembro estático o un método estático, se llamará al miembro estático o al método estático definido en la clase.
En resumen, las constantes estáticas son constantes que se determinan en el momento de la compilación y no se pueden modificar, mientras que el enlace estático es una llamada a un método o función que se determina en el momento de la compilación. Las constantes estáticas están en el nivel de clase y se puede acceder a ellas directamente a través del nombre de la clase, mientras que el enlace estático determina el método o la llamada a la función según el tipo de referencia y no se ve afectado por el tipo real del objeto.

En Java, staticy finalson palabras clave que se utilizan para definir las características de las variables. Tienen diferentes significados y usos.

Primero, echemos un vistazo a los significados de staticy finalrespectivamente static final:

  1. static: Se utiliza para indicar que la variable o método está a nivel de clase, no depende de la creación del objeto y se puede acceder a él directamente a través del nombre de la clase. Sin embargo, staticeso no significa que no se pueda modificar, las variables estáticas se pueden modificar mientras el programa se está ejecutando.

  2. final: Se utiliza para representar constantes, que no se pueden modificar una vez asignadas. Se puede utilizar para variables, métodos, clases, etc.

  3. static final: Combina staticy finaljuntos para representar una constante no modificable a nivel de clase.

Ahora, apliquemos los conceptos anteriores al código y pensemos en el resultado paso a paso:

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);
    }
}

Tenga en cuenta que intentar modificar los valores de las variables finaly static finaldará como resultado un error de compilación porque están declarados como constantes no modificables.

Insertar descripción de la imagen aquí

Resumir:
  • staticRepresenta variables o métodos de nivel de clase a los que se puede acceder directamente a través del nombre de la clase.
  • finalRepresenta una constante que no se puede modificar una vez asignada.
  • static finalRepresenta una constante no modificable a nivel de clase.
  • staticy finalse pueden utilizar de forma independiente o conjunta.Insertar descripción de la imagen aquí
- Definición en clase de Matemáticas:
 /**
     * 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;

Insertar descripción de la imagen aquí

2.Encuadernación dinámica :

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

El enlace dinámico se refiere a determinar la llamada de un método o función en función del tipo real del objeto en tiempo de ejecución. En el enlace dinámico, la invocación de un método o función se determina en función del tipo real (también conocido como tipo de tiempo de ejecución). Cuando un programa llama a un método o función en tiempo de ejecución, el tipo real del objeto se utiliza para determinar qué método o función se llama. Por lo tanto, las llamadas a métodos o funciones vinculadas dinámicamente se determinan en tiempo de ejecución en función del tipo real del objeto.

  • Ocurre en tiempo de ejecución.
  • Adecuado para situaciones en las que la llamada al método depende del tipo real del parámetro implícito.
  • El método a llamar se determina en tiempo de ejecución en función del tipo real de objeto y la selección del método se basa en el tipo real de objeto.
  • Requiere una búsqueda real en cada llamada a método, por lo que es relativamente lento, pero proporciona mayor flexibilidad y polimorfismo.

El enlace dinámico es adecuado para las siguientes situaciones:

  1. Llamar a métodos de instancia no privados, no estáticos y no finales: estos métodos pueden ser anulados por subclases, por lo que cuando se llaman estos métodos, qué método llamar se determinará en función del tipo real del objeto.
  2. Llamar a métodos de interfaz: los métodos de interfaz son definiciones de métodos abstractos y las clases de implementación necesitan implementar métodos de interfaz, por lo que al llamar a métodos de interfaz, qué método llamar se determinará en función del tipo real de la clase de implementación.

Ejemplo de código:

动态绑定是指在运行时期根据对象的实际类型来确定方法或函数的调用。
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(); // 输出:"猫发出喵喵声"
    }
}

Definimos Animalla clase usando y sus dos subclases Dogy Cat.
AnimalHay un makeSound()método en la clase, y las subclases Dogy Catsubclases anulan este método respectivamente y proporcionan sus propias implementaciones.
En mainel método, creamos un Animalobjeto animal1y dos objetos de subclase animal2y animal3.

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

animal1.makeSound()Lo que se llama es el método Animalde la clase makeSound().
animal2.makeSound()Lo que se llama es el método Dogde la clase makeSound().
animal3.makeSound()Lo que se llama es el método Catde la clase makeSound().
Esta es la característica del enlace dinámico, que permite al programa determinar el método a llamar en función del tipo real del objeto en tiempo de ejecución, en lugar de en función del tipo de referencia.

Resumir:

La característica de enlace dinámico permite que los objetos de subclase llamen al método apropiado en tiempo de ejecución según el tipo real, en lugar de según el tipo en tiempo de compilación. Esto hace que el código sea más adaptable. Cuando se introduce una nueva subclase, los nuevos métodos específicos de la subclase se pueden llamar sin modificar el código existente.

Para garantizar que cuando se anula (anula) un método de superclase en una subclase, los derechos de acceso del método de la subclase no pueden ser inferiores a los derechos de acceso del método de superclase. Si el método de la superclase es público, entonces el método de la subclase también debe declararse público. Si se viola esta regla, el compilador informará un error, porque esto puede hacer que no se pueda acceder normalmente al método heredado en algunos casos.


  1. A 1: La máquina virtual Java (JVM) es responsable de analizar y ejecutar programas Java en tiempo de ejecución [ generados y mantenidos en base a archivos de código de bytes compilados. 】. Cuando se inicia el programa Java, la JVM cargará el archivo de código de bytes y almacenará la información del método de la clase en el área de método (Área de método). Incluyendo la creación de una tabla de métodos [también conocida como tabla de métodos virtuales __vtable].
    La tabla de métodos se utiliza para almacenar la información del método de instancia y la dirección de llamada de la clase, a fin de realizar enlaces dinámicos y llamadas polimórficas en tiempo de ejecución. Por lo tanto, la JVM completa la formación de la tabla de métodos mientras el programa se está ejecutando. [La estructura y el método de almacenamiento de la tabla de métodos están definidos por la JVM, por lo que diferentes implementaciones de JVM pueden tener algunas diferencias en los detalles.
    A 2: La tabla de métodos es una estructura de datos que la máquina virtual precalcula para cada clase [almacenando información del método]. Se utiliza para registrar las firmas de todos los métodos en la clase y los métodos reales que se llamarán [contiene todos los métodos definidos en la clase] información, incluido el nombre del método, tipos de parámetros, tipo de retorno, modificadores de acceso, etc. 】.
    Cuando el programa se está ejecutando y se llama a un método mediante enlace dinámico, la máquina virtual buscará el método correspondiente en la tabla de métodos de acuerdo con el tipo real del objeto. Si se encuentra el método correspondiente al tipo real, se llama; si no se encuentra, se busca en la clase padre.Hasta que se encuentre el método correspondiente o se alcance la clase principal de nivel superior.[La función principal de la tabla de métodos es admitir el enlace dinámico y el polimorfismo en tiempo de ejecución.
    Para 3: Cuando el programa se está ejecutando, cuando se llama a un método, la JVM determinará el método específico que se llamará en función de la información de la tabla de métodos. Dado que la tabla de métodos registra la dirección real o el desplazamiento del método, se puede lograr el polimorfismo, es decir, qué método específico llamar se determina en tiempo de ejecución en función del tipo real del objeto.
    Cabe señalar que la tabla de métodos se genera en tiempo de compilación, no dinámicamente en tiempo de ejecución . Por lo tanto, para los métodos agregados dinámicamente o los métodos generados mediante el mecanismo de reflexión, la tabla de métodos no contendrá información sobre estos métodos. ↩︎

  2. El tipo de valor de retorno de una clase Java puede ser cualquier tipo de datos válido, incluidos tipos de datos primitivos (como int, double.char, etc.), tipos de objetos (como clases personalizadas, String, etc.), tipos de matrices y pronto. El tipo de valor de retorno específico depende de la definición e implementación del método. ↩︎

Supongo que te gusta

Origin blog.csdn.net/m0_74154295/article/details/132143979
Recomendado
Clasificación