¿En qué orden se ejecutan los bloques del inicializador estático/de instancia en Java?


Digamos que un proyecto contiene varias clases, cada una de las cuales tiene un bloque inicializador estático. ¿En qué orden corren esos bloques? Sé que dentro de una clase, tales bloques se ejecutan en el orden en que aparecen en el código. He leído que es lo mismo en todas las clases, pero algún código de ejemplo que escribí no está de acuerdo con eso. Usé este código:

package pkg;

public class LoadTest {
    public static void main(String[] args) {
        System.out.println("START");
        new Child();
        System.out.println("END");
    }
}

class Parent extends Grandparent {
    // Instance init block
    {
        System.out.println("instance - parent");
    }

    // Constructor
    public Parent() {
        System.out.println("constructor - parent");
    }

    // Static init block
    static {
        System.out.println("static - parent");
    }
}

class Grandparent {
    // Static init block
    static {
        System.out.println("static - grandparent");
    }

    // Instance init block
    {
        System.out.println("instance - grandparent");
    }

    // Constructor
    public Grandparent() {
        System.out.println("constructor - grandparent");
    }
}

class Child extends Parent {
    // Constructor
    public Child() {
        System.out.println("constructor - child");
    }

    // Static init block
    static {
        System.out.println("static - child");
    }

    // Instance init block
    {
        System.out.println("instance - child");
    }
}

Y obtuvo esta salida:

START
static - grandparent
static-parent
static - child
instancia - abuelo
constructor-grandparent
instancia-padre
constructor-padre
instance-child
constructor-hijo
END

La respuesta obvia es que los bloques de los padres corren antes que los de sus hijos, pero eso podría ser una coincidencia y no ayuda si dos clases no están en la misma jerarquía.

EDITAR:

He modificado mi código de ejemplo añadiendo esto a LoadTest.java:

class IAmAClassThatIsNeverUsed {
    // Constructor
    public IAmAClassThatIsNeverUsed() {
        System.out.println("constructor - IAACTINU");
    }

    // Instance init block
    {
        System.out.println("instance - IAACTINU");
    }

    // Static init block
    static {
        System.out.println("static - IAACTINU");
    }
}

Como implica el nombre de la clase, Nunca hice referencia a la nueva clase en ninguna parte. El nuevo programa produjo la misma salida que el anterior.

Author: slim, 2010-01-05

7 answers

El inicializador estático para una clase se ejecuta cuando se accede por primera vez a la clase, ya sea para crear una instancia o para acceder a un método o campo estático.

Así que, para múltiples clases, esto depende totalmente del código que se ejecuta para hacer que esas clases se carguen.

 57
Author: Chris Jester-Young,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-10 03:28:48

Consulte la sección 12.4 y 12.5 de la versión 8 de JLS, entran en detalles sangrientos sobre todo esto (12.4 para variables estáticas y 12.5 para variables de ejemplo).

Para la inicialización estática (sección 12.4):

Una clase o interfaz tipo T se inicializará inmediatamente antes de la primera aparición de cualquiera de las 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.
  • Un campo estático declarado por T es asignado.
  • 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 assert (§14.10) anidada léxicamente dentro de T (§8.1.3).

(y varias cláusulas de palabras comadreja)

 89
Author: Keith Randall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-22 14:18:56

Las respuestas de Keith y Chris son geniales, solo estoy agregando más detalles para mi pregunta específica.

Los bloques de inicio estáticos se ejecutan en el orden en que se inicializan sus clases. Entonces, ¿qué orden es ese? Según JLS 12.4.1:

Una clase o interfaz tipo T se inicializará inmediatamente antes de la primera aparición de cualquiera de las siguientes:

  • T es una clase y se crea una instancia de T.
  • T es una clase y una estática se invoca el método 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, y se ejecuta una instrucción assert (§14.10) anidada léxicamente dentro de T.

Invocación de ciertos métodos reflectantes en class Class y en package java.lang.reflect también causa inicialización de clase o interfaz. Una clase o interfaz no será inicializado bajo cualquier otra circunstancia.

Para ilustrar, aquí hay un tutorial de lo que está sucediendo en el ejemplo:

  1. Enter main
  2. Imprimir"INICIO"
  3. Intentar crear la primera instancia del Hijo, que requiere inicialización del Hijo
  4. El intento de inicializar el Hijo provoca la inicialización del Padre
  5. El intento de inicializar el Padre causa la inicialización del Abuelo
  6. Al principio de inicialización de Grandparent, el bloque de inicialización estática de Grandparent se ejecuta
  7. Técnicamente, Object tiene la última palabra en la cadena de inicialización en virtud de ser el padre del Abuelo, pero no tiene nada que contribuir
  8. Después de que finaliza el bloque de inicialización estática del Abuelo, el programa vuelve al bloque de inicialización estática del padre
  9. Después de que termina el bloque de inicialización estática del padre, el programa vuelve a la estática del Hijo bloque de inicialización
  10. En este punto, el Hijo se inicializa, por lo que su constructor puede proceder
  11. Dado que IAmAClassThatIsNeverUsed nunca se hace referencia, ninguno de sus códigos se ejecuta, incluidos los bloques de inicialización estática
  12. El resto de este tutorial no se refiere a inicializadores estáticos y se incluye solo para completar
  13. El constructor del hijo llama implícitamente a super() (es decir, el constructor del Padre)
  14. Constructor del padre implícitamente llama a super () (es decir, al constructor de Grandparent)
  15. El constructor de Grandparent hace lo mismo, lo que no tiene ningún efecto (de nuevo, Object no tiene nada que contribuir)
  16. Inmediatamente después de la llamada del constructor de Grandparent a super() viene el bloque inicializador de instancias de Grandparent
  17. El resto del constructor de Grandparent se ejecuta y el constructor termina
  18. El programa vuelve al constructor del padre, inmediatamente después de su llamada a super() (es decir, constructor de abuelos) resuelve
  19. Como anteriormente, el inicializador de instancias del padre hace lo suyo y su constructor termina
  20. De manera similar, el programa regresa y completa el constructor del Hijo
  21. En este punto, el objeto ha sido instanciado
  22. Imprimir " FIN "
  23. Terminar normalmente
 29
Author: Pops,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-05 18:38:51

La inicialización de una clase consiste en ejecutar sus inicializadores estáticos y los inicializadores para campos estáticos (variables de clase) declarados en la clase.

La inicialización de una interfaz consiste en ejecutar los inicializadores para los campos (constantes) declarados en la interfaz.

Antes de que una clase sea inicializada, su superclase directa debe ser inicializada, pero las interfaces implementadas por la clase no son inicializadas. Del mismo modo, las superinterfaces de una interfaz no son inicializado antes de inicializar la interfaz.

 1
Author: Bala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-01 12:43:54

Puede tener varios inicializadores estáticos e de instancia en la misma clase, por lo tanto

  • Los inicializadores estáticos se llaman en el orden textual en el que se declaran (desde 12.4.2)
  • Los inicializadores de instancia se llaman en el orden textual en el que se declaran (desde 12.5)

Cada uno se ejecuta como si fuera un solo bloque.

 0
Author: Martin Tapp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-25 18:21:58

Http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

Por favor revise la documentación de java.

Entonces claramente mencionado no importa cómo pueden los bloques estáticos están allí se ejecutarán como un solo bloque en el orden en que aparecen

Así que,

Mi entendimiento aquí es que java está buscando su código como

static{
i=1;
i=2;
}

Estática int i;

Es por eso que está obteniendo la salida 2

Espero que esto sea útil

 0
Author: Avinash Perla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-13 10:37:45

Hay un caso en el que un bloque estático no será llamado.

class Super {
    public static int i=10;
}
class Sub extends Super {
    static {
        system.out.println("Static block called");
    }
}
class Test {
    public static void main (String [] args) {
        system.out.println(Sub.i);
    } 
}

El código anterior produce 10

 0
Author: Java Main,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-23 15:38:01