usar programacion metodos metodo estaticos entre diferencia cuando java static-variables

java - programacion - ¿Por qué una variable estática inicializada por una llamada de método que devuelve otra variable estática permanece nula?



static final java (4)

Simplemente no entiendo el flujo de ejecución del siguiente código:

class Test { static String s1 = getVal(); static String s2 = "S2"; private static String getVal() { return s2; } public static void main(String args[]) { System.out.println(s2); // prints S2 System.out.println(s1); // prints null } }

Se supone que debe imprimir S2 en la segunda declaración de println . Estoy más interesado en entender por qué sucede esto en lugar de una solución.


Las cosas estáticas se ejecutan en el orden en que aparecen en el código.

static String s1 = getVal();

Entonces, comenzando con la primera línea, s1 se evalúa y para ese momento s2 sigue siendo null . Por lo tanto, se ve el valor nulo.


Según la sección 12.4.2 de JLS , los campos estáticos se inicializan así:

A continuación, ejecute los inicializadores de variable de clase y los inicializadores estáticos de la clase, o los inicializadores de campo de la interfaz, en orden textual , como si fueran un solo bloque.

Por lo tanto, primero se inicializa s1 , momento en el que no se inicializa s2 , por lo que tiene el valor predeterminado de String , que es null .

Y luego s2 se inicializa a "S2" pero s1 permanece null .

Solo cambia el orden de las dos declaraciones para resolver este problema.


static variables static y static bloques de inicialización static se inicializan en el orden en que aparecen en el código fuente (excepto las variables static final que se inicializan antes que las variables static no finales).

s1 se inicializa antes que s2 , por lo tanto, la llamada a getVal() devuelve el valor predeterminado de s2 , que es null .

Puede cambiar el orden de las variables static para inicializar s2 primero:

static String s2 = "S2"; static String s1 = getVal();

Otra forma de forzar la inicialización de s2 antes de la de s1 es hacer que s2 final:

static String s1 = getVal(); static final String s2 = "S2";

A continuación se muestra un extracto de la orden de inicialización como se indica en JLS 12.4.2. Procedimiento de Inicialización Detallado . Las secciones relacionadas con las variables static están resaltadas.

Para cada clase o interfaz C, hay un bloqueo de inicialización único LC. La asignación de C a LC se deja a discreción de la implementación de la Máquina Virtual de Java. El procedimiento para inicializar C es el siguiente:

  1. Sincronice en el bloqueo de inicialización, LC, para C. Esto implica esperar hasta que el subproceso actual pueda adquirir LC.

  2. Si el objeto Clase para C indica que la inicialización está en progreso para C en algún otro subproceso, entonces libere el LC y bloquee el subproceso actual hasta que se le informe de que la inicialización en curso ha finalizado, en cuyo momento repita este paso.

  3. Si el objeto de clase para C indica que la inicialización está en progreso para C por el subproceso actual, entonces esta debe ser una solicitud recursiva para la inicialización. Suelte LC y complete normalmente.

  4. Si el objeto Clase para C indica que C ya se ha inicializado, no se requiere ninguna acción adicional. Suelte LC y complete normalmente.

  5. Si el objeto Clase para C está en un estado erróneo, la inicialización no es posible. Suelte LC y lance un NoClassDefFoundError.

  6. De lo contrario, registre el hecho de que el subproceso actual está realizando la inicialización del objeto Clase para C y suelte la tecla LC.

    Luego, inicialice los campos estáticos de C, que son variables constantes (§4.12.4, §8.3.2, §9.3.1).

  7. A continuación, si C es una clase en lugar de una interfaz, deje que SC sea su superclase y que SI1, ..., SIN sean todas las superinterfaces de C que declaren al menos un método predeterminado. El orden de las superinterfaces viene dado por una enumeración recursiva sobre la jerarquía de superinterfaces de cada interfaz implementada directamente por C (en el orden de izquierda a derecha de la cláusula de implementos de C). Para cada interfaz implementada directamente por C, la enumeración se repite en las superinterfaces de I (en el orden de izquierda a derecha de la cláusula de extensión de I) antes de devolver I.

    Para cada S en la lista [SC, SI1, ..., SIn], si S todavía no se ha inicializado, realice de forma recursiva todo este procedimiento para S. Si es necesario, verifique y prepare S primero.

    Si la inicialización de S se completa abruptamente debido a una excepción lanzada, adquiera LC, etiquete el objeto Class para C como erróneo, notifique a todos los subprocesos en espera, libere LC y complete abruptamente, lanzando la misma excepción que resultó de inicializar S.

  8. A continuación, determine si las aserciones están habilitadas (§14.10) para C consultando su cargador de clases de definición.

  9. A continuación, ejecute los inicializadores de variable de clase y los inicializadores estáticos de la clase, o los inicializadores de campo de la interfaz, en orden textual, como si fueran un solo bloque .


s1 se inicializa primero y el valor de s2 es nulo en ese momento. s1 está intentando devolver el valor de s2 que se ha inicializado más adelante. Es por eso que lo estás recibiendo como nulo.

Si lo intentas así obtendrás la respuesta esperada ''S2''

class Test { static String s2 = "S2"; static String s1 = getVal(); private static String getVal() { return s2; } public static void main(String args[]) { System.out.println(s2); // prints S2 System.out.println(s1); // prints S2 } }