2010-04-09 16 views
11

Esto no es un código válido:en un inicializador estático

public class MyClass 
{ 
    private static boolean yesNo = false; 

    static 
    { 
     if (yesNo) 
     { 
      System.out.println("Yes"); 
      return; // The return statement is the problem 
     } 
     System.exit(0); 
    } 
} 

Este es un ejemplo tonto, pero en un constructor de la clase estática que no podemos return;. ¿Por qué? ¿Hay buenas razones para esto? ¿Alguien sabe algo más sobre esto?

Así que la razón por la que debería hacer return es terminar de construir allí.

Gracias

+4

Se llaman inicializadores estáticos, no constructores estáticos. Simplemente escribiendo esto para ayudar con las búsquedas. – Oak

+0

@Oak: Gracias (15 caracteres) –

Respuesta

15

Creo que la razón es que los inicializadores se llevan junto con las inicializaciones de campo (y con los constructores, en el caso de los inicializadores de instancias). En otras palabras, la JVM solo reconoce un lugar para inicializar campos estáticos y, por lo tanto, todas las inicializaciones, ya sea en bloques o no, deben realizarse allí.

Así, por ejemplo, cuando se escribe una clase:

class A { 
    static int x = 3; 
    static { 
     y = x * x; 
    } 
    static int z = x * x; 
} 

A continuación, en realidad es como si usted ha escrito:

class A { 
    static int x, y, z; 
    static { 
     x = 3; 
     y = x * x; 
     z = x * x; 
    } 
} 

Esto se confirma si nos fijamos en el desmontaje:

static {}; 
    Code: 
    0: iconst_3 
    1: putstatic  #5; //Field x:I 
    4: getstatic  #5; //Field x:I 
    7: getstatic  #5; //Field x:I 
    10: imul 
    11: putstatic  #3; //Field y:I 
    14: getstatic  #5; //Field x:I 
    17: getstatic  #5; //Field x:I 
    20: imul 
    21: putstatic  #6; //Field z:I 
    24: return 

Así que si hubiera agregado un "retorno" en algún lugar en el medio de su inicializador estático, también habría evitó que z se calculara.

+1

Estaba a punto de escribir eso cuando vi esto. También tenga en cuenta que puede tener múltiples bloques de inicializadores estáticos en una clase. –

+0

Muy buena respuesta, Oak. También muestra por qué una finalización abrupta se considera tan mala en un inicializador estático (como señaló Joe en un comentario) para dar un error en tiempo de compilación. – jalopaba

2

¿A qué usted debe volver? En un inicializador estático no hay llamadas, por lo que un retorno no tiene sentido por lo que yo veo. Los inicializadores estáticos se ejecutan cuando la clase se carga por primera vez.

10
  • el flujo de programa siempre se puede estructurar para ir sin la necesidad de return. (En el ejemplo poniendo System.exit(0) en una cláusula else permita lograr el resultado deseado)

  • en que realmente lo necesite, puede mover el código en un método estático y llamarlo desde el inicializador:

.

static { 
    staticInit(); 
} 

private static void staticInit() { 
    if (yesNo) { 
     System.out.println("Yes"); 
     return; 
    } 
    System.exit(0); 
} 

Tenga en cuenta que esto no es un constructor estático, es un inicializador estático. Nada se construye.

+0

Sé que puedes resolverlo con un simple 'else'. Pero dije que era un ejemplo muy estúpido. ¡Pero es verdad! –

+0

@Martijn Courteaux sí, lo entendí. Es por eso que está entre paréntesis, solo un ejemplo de mi generalización. – Bozho

1

De JSL regarding static initializers:

"es un error de tiempo de compilación para un inicializador estático para poder completar bruscamente (§14.1, §15.6) con una excepción comprobada (§11.2) Es una de compilación. error de tiempo si un inicializador estático no puede completarse normalmente (§14.21). "

Abrupt completion (entre otros): "retorno sin valor", "volver con un valor dado", etc.

lo tanto, una sentencia de retorno en un inicializador estático es una "terminación abrupta" y produce una de compilación error de tiempo

+1

El JSL no especifica una razón, sin embargo, de eso se trata esta pregunta. – Oak

+1

Estaba a punto de publicar lo mismo, pero estaba tratando de pensar: ¿por qué es tan abrupta la finalización de un inicializador estático que lo convirtieron en un error de compilación? –

+0

@Oak: tienes razón, no hay razón para eso. Las especificaciones son a veces como axiomas. – jalopaba

0

Entiendo que la regla para los inicializadores estáticos es que solo se ejecutan una vez, después de cargar el código de byte de clase y antes de ejecutar cualquier método estático o instanciar el primer objeto de la clase. El JLS garantiza que esta inicialización se habrá completado. Para garantizar que esta garantía sea verdadera, el JLS también especifica que el código no puede haber sido cancelado abruptamente (como se indica claramente en otra respuesta).

Tenga en cuenta que es posible cargar código de bytes sin inicializarlo; ver el método Class.forName(String, boolean, ClassLoader). Si el parámetro boolean es false, esto cargará la clase pero no la inicializará. El programador aún puede hacer una reflexión para descubrir información sobre esa clase aún sin haber sido inicializada. Sin embargo, una vez que intenta utilizar la clase directamente llamando a un método estático o instanciando una instancia, entonces la JVM procederá a inicializarla primero.

Si cualquiera de los inicializadores estáticos finaliza abruptamente, lo que puede suceder con un RuntimeException, la clase se dejará en un estado no válido. La primera vez, la JVM lanzará un ExceptionInInitializeError (aviso que es un Error lo que significa que se considera una falla interna). A partir de ese momento, no será posible utilizar la clase; al intentar llamar a un método estático o al crear una instancia de un objeto, obtendrás un NoClassDefFoundError.

La única forma de recuperarse de esta situación sin reiniciar la JVM es si está utilizando ClassLoader sy puede reemplazar el cargador de clases con la clase fallida y reconstruir la clase o reiniciador en un entorno diferente (quizás propiedades de sistema diferentes), pero el programa debe estar bien preparado para este tipo de situación.

0

Reordenaré la declaración, haciéndola más simple/más corta. Nunca habrá un buen caso en el que ambas ramas de if/else necesiten un retorno.

static { 
    if (!yesNo) 
     System.exit(0); // silently exiting a program is a bad idea!" 
    System.out.println("Yes"); 
} 
Cuestiones relacionadas