2010-03-11 22 views
7

El programa siguiente imprime:Java problema orden de inicialización, estática vs ejemplo campos

my name is:null 

my name is:null 

Someclass static init 

yo sepa cuando se carga por primera vez una clase de bloques estáticos y campos siempre se inicializan en primer lugar, los bloques de instancias y campos segundos. Por lo tanto, las variables "objectName1" y "objectName2" deben inicializarse primero, la variable de instancia "list" second ... pero la salida obviamente contradice esta teoría ... ¿Alguien puede explicar el comportamiento del programa (no estoy buscando una crítica del diseño en sí mismo por cierto)?

import java.util.ArrayList; 
import java.util.List; 

public class Main2{ 
    public static void main (String[] args){ 
     SomeClass.getInstance(); 
    } 
} 

class SomeClass { 
    private static final SomeClass instance = new SomeClass(); 

    public static SomeClass getInstance(){ 
     return instance; 
    } 

    static { 
     System.out.println ("Someclass static init"); 
    } 
    private static String objectName1 ="test1"; 
    private static String objectName2 ="test2"; 

    @SuppressWarnings("serial") 
    private List<SomeObject> list= 
     new ArrayList<SomeObject>() { { 
add (new SomeObject(objectName1)); 
add (new SomeObject(objectName2)); 
    }}; 
} 

class SomeObject { 
    String name; 
    SomeObject (String name){ 
     this.name = name; 
     System.out.println ("my name is:" +name); 
    } 
} 

Respuesta

10

bloques estáticos se inicializan con el fin (por lo que pueden confiar en los de arriba en los de abajo). Al crear una instancia de SomeClass como su primer inicializador estático en SomeClass, está forzando una instancia init durante la fase de inicio estático.

Así que el orden lógico de ejecución de su código es:

  • SomeClass clase de carga, todos los campos estáticos inicialmente por defecto (0, null, etc.)
  • Begin estática ensu
  • Primera init estática crea la instancia de SomeClass
  • Comienza la instancia de entrada para la instancia SomeClass, usando los valores actuales para los campos estáticos (entonces objectName1 y objectName2 son null)
  • carga SomeObject clase, todos los campos estáticos inicialmente por defecto (que no tiene ninguna)
  • ¿Es SomeObject estática ensu (que no tiene ninguna)
  • crear instancias de SomeObject utilizando el passed- en null valores
  • Continuar estática INITs de SomeClass, el establecimiento de objectName1 y objectName2

Para realizar este trabajo, como cabe esperar, en pocas palabras los inits para objectName1 y objectName2 sobre el init para instance.

0

Lo primero que se ejecuta es probablemente el inicializador estático de la variable instance. Esto hace que la lista se inicialice utilizando las variables (no inicializadas) objectName1 y objectName2. Después de eso, procede a inicializar objectName1 y objectName2.

Si mueve la declaración de instance al final de SomeClass, probablemente haga lo que está esperando.

1

como se sugiere en movimiento esta línea:

private static final SomeClass instance = new SomeClass(); 

después de que éstos:

private static String objectName1 ="test1"; 
private static String objectName2 ="test2"; 

debería solucionar el problema.

1

A primera vista me sorprendió bastante sobre el comportamiento a mí mismo, pero pensándolo bien, es bastante trivial para explicar:

private static final SomeClass instance = new SomeClass(); 

es parte de la inicialización estática de SomeClass. A medida que crea una instancia antes de que se complete la inicialización, la clase aún no se ha inicializado por completo. Cuando sustituya la System.out.println(...); con algo como new Exception().printStackTrace(); obtiene este (nota que puse todas las clases como clases anidadas estáticas en Principal)

at Main$SomeObject.<init>(Main.java:37) // new Exception().printStackTrace(); 
at Main$SomeClass$1.<init>(Main.java:26) // add(new SomeObject(...)) 
at Main$SomeClass.<init>(Main.java:23) // list = new ArrayList() 
at Main$SomeClass.<clinit>(Main.java:10) // instance = new SomeClass() 
at Main.main(Main.java:6) // SomeClass.getInstance(); 

Como se ve, la ejecución aún está dentro de Main$SomeClass.<clinit> (la inicialización de clase), por lo tanto, SomeClass no está completamente inicializado

Como nota al margen: la mejor manera de implementar el patrón Singleton es evitarlo por completo. La segunda mejor opción es usar enum (al menos está aprobado por Josh-Bloch)

class enum SomeClass { 
    instance; 

    // snip 
} 
Cuestiones relacionadas