Prefacio
En Java, un objeto debe inicializarse correctamente antes de que pueda usarse, lo que está estipulado por la especificación de Java. Recientemente descubrí una pregunta interesante, y la respuesta a esta pregunta me engañó a primera vista. Eche un vistazo a estas tres categorías:
paquete com.ds.test; public class Upper {String UperperString; public Upper () {Inicializer.initialize (this); }} paquete com.ds.test; public class Lower extiende superior {string lisuTRing = null; public lower () {super (); System.out.println ("superior:" + upperstring); System.out.println ("Lower:" + LowerString); } public static void main (string final [] args) {new Lower (); }} paquete com.ds.test; public class inicializer {static void inicialize (final anupper final) {if (anupper instancia de baja) {inferior inferior = (inferior) anupper; Lower.lowerString = "LowerInited"; } anupper.upperstring = "superiorinited"; }} ¿Qué salida se puede obtener ejecutando la clase Lower ? En este ejemplo mínimo, toda la situación se puede ver más fácilmente, pero esta situación ocurre en la realidad y habrá mucho código que distrae a una persona.
De todos modos, la salida es así:
Superior: superiorinitedlower: nulo;
Aunque el tipo String se usa en el pequeño ejemplo, el código real de la clase Initializer tiene un objeto delegado para el registro, que es el mismo que Lower , al menos Lower es la intención. Pero por alguna razón no funciona al ejecutar la aplicación. En su lugar, se usa la ruta predeterminada y el objeto delegado no está configurado (NULL).
Ahora cambie un poco el código de Lower :
paquete com.ds.test; public class Lower extiende superior {string lowerString; public lower () {super (); System.out.println ("superior:" + upperstring); System.out.println ("Lower:" + LowerString); } public static void main (string final [] args) {new Lower (); }}La salida ahora se ve así:
Superior: superiorinitedLower: LowerInited
¿Has encontrado la diferencia en el código?
Sí, este campo lowerString ya no se establece explícitamente en vacío. ¿Por qué esto lo hace diferente? De todos modos, ¿no está vacío el valor predeterminado del campo Tipo de referencia (como String aquí)? Por supuesto que está vacío. Resulta que si bien este ligero cambio obviamente no cambia el comportamiento del código de ninguna manera, hace que el resultado sea diferente.
Entonces, ¿qué pasó exactamente? Todo queda claro al verificar el orden de inicialización:
1. main() llama al constructor Lower .
2. Una instancia de Lower está lista. Lo que significa que todos los campos se crean y se llenan con valores predeterminados, por ejemplo, el valor predeterminado del tipo de referencia está vacío y el valor predeterminado del tipo booleano es false . En este momento, no se produce una asignación en línea al campo.
3. Se llama al constructor de la clase principal. Esto se aplica por las características del lenguaje. Entonces, antes de que ocurra cualquier otra cosa, se llama al constructor de Upper.
4. Super se ejecuta este constructor y especifica una referencia a la instancia recientemente creada del método Initializer.initialize() .
5. Initializer adjunta una nueva cadena a dos campos ( upperString y lowerString ). Asignar valores a esos dos campos utilizando una instanceof sucia de verificación de instancias no es un patrón de diseño particularmente bueno, pero también funciona, no se preocupe por eso. Una vez que eso sucede, las referencias a upperString y lowerString ya no están vacías.
6. La llamada a Initializer.initialize() se completa y Upper también se completa.
7. Ahora se está volviendo interesante: la construcción de Lower continúa. Suponiendo que la asignación =null no se especifica explícitamente en la declaración del campo lowerString , el constructor Lower reanuda la ejecución e imprime dos cadenas conectadas al campo.
Sin embargo, si hay una operación que asigna explícitamente NULL, el proceso de ejecución será ligeramente diferente: cuando se complete el constructor de clase principal, cualquier inicialización de variable se realizará antes de que se ejecute el resto de los constructores (consulte la Sección 12.5 de la especificación de lenguaje Java). En este caso, la referencia de cadena asignada a lowerString anteriormente no se asigna nuevamente NULL. Luego continúe ejecutando la construcción de la función restante, y ahora imprima el valor de lowerString como: NULL.
Este es un gran ejemplo, no solo para facilitar nuestra atención a algunos detalles sobre la creación de objetos (o saber dónde consultar las especificaciones de codificación de Java, impresos o en línea), sino también para mostrar por qué escribir inicializaciones como esta es mala. No deberíamos preocuparnos por la subclase de Upper en absoluto. Por el contrario, si por alguna razón la inicialización de ciertos campos no se puede hacer en la subclase en sí, solo requerirá alguna variante de su propia clase de inicentilización auxiliar. En este caso, si usa String lowString o String lowerString = null realmente no hay diferencia, lo que debería ser.
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo sea de ayuda para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse.