Tres características principales de la programación orientada a objetos: encapsulación, herencia y polimorfismo
Encapsulación significa literalmente empaquetar. El punto profesional es ocultar información, que se refiere al uso de tipos de datos abstractos para encapsular datos y operaciones basadas en datos para formar una entidad independiente indivisible. Los datos están protegidos dentro de los datos abstractos. tipo, ocultando los detalles internos tanto como sea posible y conservando solo algunas interfaces externas para conectarlo con el exterior. Otros objetos en el sistema solo pueden comunicarse e interactuar con este objeto encapsulado a través de operaciones autorizadas envueltas fuera de los datos. Es decir, el usuario no necesita conocer los detalles internos del objeto, pero puede acceder al objeto a través de la interfaz proporcionada por el objeto.
Para la encapsulación, un objeto encapsula sus propias propiedades y métodos, por lo que puede completar sus propias operaciones sin depender de otros objetos. Hay tres beneficios principales de usar la encapsulación:
La encapsulación privatiza las propiedades de un objeto y proporciona algunos métodos para las propiedades a las que puede acceder el mundo exterior. Si no queremos que nos accedan mediante métodos externos, No necesitamos proporcionar métodos para el acceso externo. Pero si una clase no proporciona métodos para acceso externo, entonces esta clase no tiene sentido.
La encapsulación nos permite modificar fácilmente la implementación interna de una clase sin modificar el código del cliente que usa la clase. Puede tener un control más preciso sobre las variables miembro.
2.1 Descripción general de la herencia
La herencia es una tecnología que utiliza la definición de una clase existente como base para crear una nueva clase. La definición de una nueva clase puede agregar nuevos datos o. nuevas funciones, o puede Utilice las funciones de la clase principal, pero no puede heredar selectivamente la clase principal. Al utilizar la herencia, podemos reutilizar el código anterior de manera muy conveniente, lo que puede mejorar en gran medida la eficiencia del desarrollo.
La herencia describe la relación "es-a". Si hay dos objetos A y B, si se puede describir como "A es B", puede significar que A hereda B, donde B es el. objeto. Los herederos lo llaman clase padre o superclase, y A es el heredero llamado subclase o clase derivada.
De hecho, el sucesor es una especialización del heredado, además de tener las características del heredado, también tiene sus propias características únicas. Por ejemplo, los gatos tienen características que otros animales no tienen, como cazar ratones y trepar a los árboles. Al mismo tiempo, en la relación de herencia, el heredero puede reemplazar completamente lo heredado, pero no al revés. Por ejemplo, podemos decir que un gato es un animal, pero no podemos decir que el animal sea en realidad un gato. razón por la que a esto lo llamamos "transformación ascendente".
De hecho, la herencia define cómo las clases se relacionan entre sí y comparten características. Para varias clases idénticas o familiares, podemos abstraer sus comportamientos o atributos únicos y definirlos como una clase principal o superclase, y luego usar estas clases para heredar la clase principal. No solo pueden tener las propiedades y métodos de la clase principal. definir sus propias propiedades o métodos únicos.
Al mismo tiempo, debes recordar tres oraciones al usar la herencia:
Estas tres cosas son indispensables para aprender la herencia: constructor, palabra clave protegida y transformación ascendente
2.2 Constructor
Del artículo anterior, sabemos que las subclases pueden heredar los atributos y métodos de la clase principal. Además de los privados, hay otra cosa que las subclases no pueden heredar: constructores. En cuanto al constructor, solo se puede llamar y no se puede heredar. Para llamar al constructor de la clase padre, podemos usar super().
El proceso de construcción se extiende "hacia afuera" desde la clase principal, es decir, comenzando desde la clase principal y completando la construcción paso a paso hasta las subclases. Y no hicimos referencia explícita al constructor de la clase principal. Esta es la inteligencia de Java: el compilador llamará al constructor de la clase principal para la subclase de forma predeterminada.
Sin embargo, esta llamada predeterminada al constructor de la clase principal tiene un requisito previo: la clase principal tiene un constructor predeterminado. Si la clase principal no tiene un constructor predeterminado, debemos usar explícitamente super() para llamar al constructor de la clase principal; de lo contrario, el compilador informará un error: No se puede encontrar un constructor que se ajuste a la forma de la clase principal.
Para las subclases, la inicialización correcta de su constructor es muy importante, y solo si solo hay un método que puede garantizar esto: llamar al constructor de la clase principal en el constructor para completar la inicialización, y El constructor de la clase principal tiene todos los conocimientos y capacidades necesarios para realizar la inicialización de la clase principal.
Para la herencia, la subclase llamará al constructor de la clase principal de forma predeterminada, pero si no hay un constructor de clase principal predeterminado, la subclase debe especificar explícitamente el constructor de la clase principal y debe estar en Lo primero que se hace en el constructor de la subclase (la primera línea de código).
2.3 palabra clave protegida
El modificador de acceso privado es la mejor opción para la encapsulación, pero esto solo se basa en un mundo ideal. A veces necesitamos tales requisitos: necesitamos ocultar ciertas cosas. del mundo tanto como sea posible, pero aún permite que los miembros de las subclases accedan a ellos. En este momento necesitas usar protected.
Para protegido, indica que es privado en lo que respecta al usuario de la clase, pero es privado para cualquier subclase que herede de esta clase o cualquier otra clase en el mismo paquete sea accesible.
2.4 Transformación ascendente
En la herencia anterior, hablamos de que la herencia es una relación es-a. La herencia del gato está relacionada con los animales, por lo que podemos decir que los gatos son animales, o. gatos Es una especie de animal. Pensar en los gatos como animales de esta manera es transformarse hacia arriba.
Aquí pasamos Person.display(marido). De esta frase se puede ver que el marido es un tipo de persona.
La conversión de una subclase en una clase principal avanza hacia arriba en la relación de herencia, por lo que generalmente se denomina transformación ascendente. Dado que la conversión ascendente es una conversión de un tipo especializado a un tipo más general, siempre es seguro. El único cambio que puede ocurrir es la pérdida de propiedades y métodos. Esta es la razón por la que el compilador permite conversiones ascendentes incluso si la conversión no se indica explícitamente o si no se especifica ningún indicador especial.
2.5 Tenga cuidado con la herencia
Aquí debemos dejar claro que la herencia tiene los siguientes defectos:
Entonces, cuando usamos la herencia, debemos hacer Asegúrese de que es correcto utilizar la herencia. Es un método eficaz y factible. Entonces, ¿deberíamos utilizar la herencia? "Think in Java" proporciona una solución: pregúntese si necesita realizar una transformación ascendente de la subclase a la clase principal. Si es necesaria una transformación ascendente, la herencia es necesaria, pero si no, debes considerar cuidadosamente si necesitas herencia.
3.1 Descripción general del polimorfismo
El llamado polimorfismo significa que el tipo específico al que apunta la variable de referencia definida en el programa y las llamadas al método emitidas a través de la variable de referencia no están determinadas durante la programación se determina durante la ejecución del programa, es decir, a qué objeto de instancia de clase apuntará una variable de referencia, y la llamada al método emitida por la variable de referencia es un método implementado en qué clase, que debe determinarse durante la ejecución. ejecución del programa. Debido a que la clase específica se determina solo cuando el programa se está ejecutando, la variable de referencia se puede vincular a varias implementaciones de clase sin modificar el código fuente del programa, lo que hace que el método específico llamado por la referencia cambie en consecuencia, es decir, no es necesario modificarse. El código del programa puede cambiar el código específico vinculado al programa cuando se está ejecutando, lo que permite que el programa seleccione múltiples estados de ejecución. Esto es polimorfismo.
Entonces podemos resumir el polimorfismo de la siguiente manera:
Debido a la transformación ascendente de la referencia de la clase principal que apunta a la subclase, solo puede acceder a los métodos y propiedades propiedad de la clase principal. Y para los métodos que existen en la subclase pero no en la clase principal, la referencia no se puede utilizar, incluso si el método está sobrecargado. Si una subclase anula algunos métodos en la clase principal, al llamar a estos métodos, debe usar los métodos definidos en la subclase (conexión dinámica, llamada dinámica).
Para la orientación a objetos, el polimorfismo se divide en polimorfismo en tiempo de compilación y polimorfismo en tiempo de ejecución. Entre ellos, el polimorfismo en tiempo de edición es estático y se refiere principalmente a la sobrecarga de métodos. Distingue diferentes funciones en función de diferentes listas de parámetros, y no habrá polimorfismo en tiempo de ejecución. El polimorfismo en tiempo de ejecución es dinámico y se logra mediante enlace dinámico, que es lo que llamamos polimorfismo.
3.2 Condiciones para la realización del polimorfismo
Al principio se mencionó que la herencia prepara para la realización del polimorfismo.
La subclase Child hereda la clase principal Father. Podemos escribir una referencia de tipo de clase principal que apunte a la subclase. Esta referencia puede manejar el objeto Father de la clase principal o el objeto Child de la subclase. clase Cuando se utiliza un objeto, el objeto realizará diferentes comportamientos según la referencia a la que pertenece. Esto es polimorfismo. Es decir, polimorfismo significa que el mismo mensaje hace que diferentes clases respondan de manera diferente.
Hay tres condiciones necesarias para que Java alcance el polimorfismo: herencia, reescritura y transformación ascendente.
Herencia: En el polimorfismo, debe haber subclases y clases padre con relaciones de herencia.
Reescritura: la subclase redefine ciertos métodos en la clase principal, y cuando se llaman estos métodos, se llamarán los métodos de la subclase.
Transformación ascendente: en el polimorfismo, la referencia de la subclase debe asignarse al objeto de la clase principal. Solo así la referencia puede tener la capacidad de llamar a los métodos de la clase principal y a los métodos de la clase principal. la subclase.
Solo cuando se cumplen las tres condiciones anteriores, podemos utilizar un código de implementación de lógica unificada para procesar diferentes objetos en la misma estructura de herencia, a fin de realizar diferentes comportamientos.
Para Java, su mecanismo de implementación polimórfico sigue un principio: cuando una variable de referencia de un objeto de superclase se refiere a un objeto de subclase, el tipo del objeto al que se hace referencia, en lugar del tipo de la variable de referencia, determina a quién se llama. método, pero el método llamado debe estar definido en la superclase, es decir, un método anulado por la subclase.
3.3 Formas de implementación
Hay dos formas de polimorfismo en Java: herencia e interfaces.
3.2.1. Polimorfismo basado en herencia
El mecanismo de implementación basado en herencia se refleja principalmente en la forma en que la clase padre y una o más subclases que heredan la clase padre implementan ciertas Al anular métodos, varias subclases pueden exhibir comportamientos diferentes al anular el mismo método.
El polimorfismo implementado según la herencia se puede resumir de la siguiente manera: para un tipo de clase principal que hace referencia a una subclase, al procesar la referencia, se aplica a todas las subclases que heredan la clase principal, y los objetos de la subclase son La implementación del método también es diferente y el comportamiento producido al realizar la misma acción también es diferente.
Si la clase principal es una clase abstracta, entonces la subclase debe implementar todos los métodos abstractos en la clase principal. De esta manera, todas las subclases de la clase principal deben tener una interfaz externa unificada, pero su interfaz interna es específica. La implementación puede ser diferente. De esta manera, podemos utilizar la interfaz unificada proporcionada por la clase de nivel superior para manejar los métodos en este nivel.
3.2.2. Polimorfismo basado en la implementación de la interfaz
La herencia se refleja en varias subclases diferentes que anulan el mismo método de la clase principal, luego se puede implementar a través de interfaces incorporadas por varias clases diferentes que cubren el mismo método en la interfaz.
En el polimorfismo de la interfaz, la referencia que apunta a la interfaz debe ser un programa de instancia que especifica una clase que implementa la interfaz. En tiempo de ejecución, el método correspondiente se ejecuta de acuerdo con el tipo real del objeto. referencia.
La herencia es una herencia única, que solo puede proporcionar una interfaz de servicio consistente para un grupo de clases relacionadas. Sin embargo, las interfaces pueden tener múltiples herencias y múltiples implementaciones. Se pueden combinar y expandir utilizando un conjunto de interfaces relacionadas o no relacionadas y pueden proporcionar interfaces de servicios consistentes al mundo exterior. Por tanto, tiene mayor flexibilidad que la herencia.
3.2.3. Análisis de ejemplo clásico
Resultados de ejecución:
El análisis es el siguiente:
①②③ es relativamente fácil de entender. y generalmente no comete errores. ④⑤ es un poco confuso. ¿Por qué la salida no es "B y B"?
Cuando una variable de referencia de un objeto de superclase se refiere a un objeto de subclase, el tipo del objeto al que se hace referencia, en lugar del tipo de la variable de referencia, determina de qué método miembro se llama, pero el método llamado debe estar en la superclase Definido , es decir, métodos anulados por subclases. (Pero si fuerza la superclase a convertirse en una subclase, puede llamar a métodos recién agregados en la subclase que no están en la superclase).
Hay una prioridad para llamar a métodos de objeto en la superclase. cadena de herencia: this.show(O), super.show(O), this.show((super)O), super.show((super)O).
A, B, C, D en el programa anterior tienen la siguiente relación:
Análisis 4:
a2.show(b), a2 es un referencia El tipo de variable es A, entonces esto es a2, y b es una instancia de B, por lo que busca el método show(B obj) en la clase A, pero no puede encontrarlo, por lo que lo busca en el super (super clase), y A no tiene una superclase, por lo que pasa a la tercera prioridad this.show((super)O), esto sigue siendo a2, donde O es B, (super)O es (super)B es A , entonces va a la clase A para encontrar el método show (A obj), la clase A tiene este método, pero debido a que a2 se refiere a un objeto de la clase B, B anula el método show (A obj) de A, por lo que finalmente está bloqueado. al show (A obj) de la clase B, y la salida para "B y A".
Análisis 5:
a2.show(c), a2 es una variable de referencia de tipo A, por lo que esto representa A, a2.show(c), está en la clase A Descubrí que no podía encontrarlo, así que busqué (super) en la superclase de A. Como A no tiene superclase (excepto Objeto), salté al tercer nivel, que es este.show((super )O), la superclase de C. Las clases incluyen B y A, por lo que (super)O es B y A, y esto también es A. Show(A obj) se encuentra en A. Al mismo tiempo, dado que a2 es una referencia de la clase B y la clase B reescribe show (A obj), por lo que eventualmente se llamará al método show (A obj) de la subclase B, y el resultado es B y A.
Análisis 8:
b.show(c), b es una variable de referencia con tipo B, entonces esta es b, y c es una instancia de C, por lo que va a la clase B buscó el método show(C obj), pero no pudo encontrarlo. Recurrió a la superclase A de B para encontrarlo, pero tampoco existía en A, por lo que también recurrió al tercer nivel de prioridad. show((super)O). Esto es b. O es C, (super)O es (super)C, que es B, por lo que busca el método show(B obj) en B y lo encuentra. a un objeto de clase B, está directamente bloqueado en la clase B. show (B obj), la salida es "B y B".
Puedo confirmar otras respuestas siguiendo el mismo método.
Cuando una variable de referencia de un objeto de superclase se refiere a un objeto de subclase, el tipo del objeto al que se hace referencia, en lugar del tipo de la variable de referencia, determina de qué método miembro se llama, pero el método llamado debe estar en la superclase Definido , es decir, métodos anulados por subclases. Usemos un ejemplo para ilustrar el significado de esta oración: a2.show(b);
Aquí a2 es una variable de referencia, que es de tipo A. Se refiere al objeto B, por lo que de acuerdo con arriba El significado de la oración es que B decide qué método llamar, por lo que a2.show (b) debería llamar a show (B obj) en B, y el resultado debería ser "B y B", pero ¿por qué es diferente del ¿El anterior? ¿Cuáles son las diferencias en los resultados de carrera? Aquí ignoramos la siguiente oración "El método llamado debe definirse en la superclase", entonces, ¿existe show(B obj) en la clase A? ¡No existe! ¿Entonces esta frase no se aplica aquí? Entonces, ¿está mal esta frase? ¡No! De hecho, esta oración también implica esta oración: aún debe confirmarse de acuerdo con la prioridad del método de llamada en la cadena de herencia. Es por eso que encontrará show(A obj) en la clase A. Al mismo tiempo, debido a que B ha reescrito este método, llamará al método en la clase B. De lo contrario, llamará al método en la clase A.
Nota: Todas las preguntas de la entrevista no son estáticas, especialmente para los grandes fabricantes de primer nivel. Las preguntas de la entrevista anteriores son solo una referencia para todos. Lo más importante es aumentar su reserva de conocimientos y estar preparado para cualquier cosa. problemas.
Finalmente, me gustaría compartir con ustedes la serie de notas de estudio y preguntas de la entrevista de Spring, incluidas las preguntas de la entrevista de Spring, las preguntas de la entrevista de Spring Cloud, las preguntas de la entrevista de Spring Boot, las notas del tutorial de Spring, las notas del tutorial de Spring Boot y el último manual de desarrollo de Alibaba (63). -página de resumen en PDF), manual de entrevistas de Java 2022. Clasifiqué 1184 páginas de documentos PDF en un día. Envía un mensaje privado al blogger (777) para recibirlo, ¡deseo que todos lleguen a un nivel superior! !
Autor original: jianyuerensheng
Fuente original: /jianyuerensheng/article/details/51602015