tipos - Cómo carga JVM clases principales en Java
que es una clase en java (3)
Código:
class A {
static {
System.out.println("loading A static 1");
}
static {
System.out.println("loading A static 2 B.c= "+B.c);
}
static {
System.out.println("loading static 3");
}
static int a=10;
A(){
}
}
class B extends A{
static {
System.out.println("loading B A.a= "+A.a);
}
static int c = 50;
}
public class Test {
public static void main(String[] args) {
new B();
}
}
Salida:
loading A static 1
loading A static 2 B.c= 0
loading static 3
loading B A.a= 10
A partir de esto, ¿podemos decir que la clase padre carga después de la clase hija pero la clase secundaria se inicializa después de la clase padre? Si es así, ¿cómo JVM carga las jerarquías de clase?
el bloque estático se ejecutará cuando se inicialice una clase. y sabemos
Antes de inicializar una clase, su superclase directa debe inicializarse,
Entonces, en su caso cuando está inicializando la B, su superclase se inicializa automáticamente antes de eso.
para más detalles puede verificar esta parte de la especificación
La Especificación del lenguaje Java explica el proceso de inicialización de una clase.
Una clase o interfaz de tipo T se inicializará inmediatamente antes de la primera aparición de cualquiera de los siguientes:
T es una clase y se crea una instancia de T.
T es una clase y se invoca un método estático declarado por T.
Se asigna un campo estático declarado por T.
Se utiliza un campo estático declarado por T y el campo no es una variable constante (§4.12.4).
T es una clase de nivel superior (§7.6) y se ejecuta una declaración de afirmación (§14.10) léxicamente anidada dentro de T (§8.1.3).
[...]
Sincronice en el bloqueo de inicialización, LC, para C. Esto implica esperar hasta que el hilo actual pueda adquirir LC.
[...]
Si el objeto de clase para C indica que la inicialización está en curso para C por el hilo actual, entonces esta debe ser una solicitud recursiva para la inicialización. Libera LC y completa normalmente.
[...]
A continuación, si C es una clase en lugar de una interfaz, y su superclase SC aún no se ha inicializado, entonces recursivamente realizar todo este procedimiento para SC. Si es necesario, verifique y prepare SC primero. Si la inicialización de SC se completa abruptamente debido a una excepción lanzada, entonces adquiera LC, etiquete el objeto de Clase para C como erróneo, notifique todos los hilos en espera, suelte LC, y termine abruptamente, lanzando la misma excepción que resultó de inicializar SC.
Asi que
new B();
requiere que la clase B
sea inicializada. Como B
es una subclase de A
, A
necesita inicializarse. Al inicializar A
, esto
static {
System.out.println("loading A static 2 B.c= "+B.c);
}
indica que B
necesita inicializarse, pero B
ya está en proceso de inicialización, por lo que se ignora por ahora y la inicialización A
continúa. Como la inicialización de B
no está completa, el campo c
aún no se ha inicializado a 50, por lo que imprime 0
.
A
inicialización completa. B
inicialización continúa. B
inicialización completa y Boom! ya terminaste
Puede verificar esto ejecutándolo con java -verbose Test
:
...
[Loaded A from file:/.../src/main/java/]
[Loaded B from file:/.../src/main/java/]
loading A static 1
loading A static 2 B.c= 0
loading static 3
loading B A.a= 10
...
Entonces, no, la clase padre también se carga primero.