Perché i campi statici non sono inizializzati in tempo?

Il seguente codice stampa null una volta.

 class MyClass { private static MyClass myClass = new MyClass(); private static final Object obj = new Object(); public MyClass() { System.out.println(obj); } public static void main(String[] args) {} } 

Perché gli oggetti statici non vengono inizializzati prima dell’esecuzione del costruttore?

Aggiornare

Avevo appena copiato questo programma di esempio senza attenzione, pensavo che stessimo parlando di 2 campi Oggetto, ora ho visto che il primo è un campo MyClass ..: /

Perché le statistiche vengono inizializzate nell’ordine in cui sono fornite nel codice sorgente.

Controllalo:

 class MyClass { private static MyClass myClass = new MyClass(); private static MyClass myClass2 = new MyClass(); public MyClass() { System.out.println(myClass); System.out.println(myClass2); } } 

Quello stamperà:

 null null myClassObject null 

MODIFICARE

Ok, facciamo in modo che sia un po ‘più chiaro.

  1. Le statistiche sono inizializzate una per una nell’ordine dichiarato nel codice sorgente.
  2. Poiché la prima statica è inizializzata prima del resto, durante l’inizializzazione il resto dei campi statici sono valori nulli o predefiniti.
  3. Durante l’avvio della seconda statica, la prima statica è corretta ma il resto è ancora nullo o predefinito.

È chiaro?

MODIFICA 2

Come Varman ha sottolineato, il riferimento a se stesso sarà nullo mentre viene inizializzato. Il che ha senso se ci pensi.

Proviamo un modo diverso per spiegare questo …

Questa è la sequenza in cui passa la JVM quando si fa riferimento per la prima volta alla class MyClass .

  1. Carica il codice byte in memoria.
  2. La memoria per la memoria statica viene cancellata (zero binario).
  3. Inizializza la class:
    1. Esegui ogni inizializzatore statico nell’ordine in cui appare, questo include variabili static { ... } e blocchi static { ... } .
    2. JVM quindi inizializza la variabile statica myClass in una nuova istanza di MyClass .
    3. Quando ciò accade, la JVM nota che MyClass è già stato caricato (codice byte) e nel processo di inizializzazione , quindi ignora l’inizializzazione.
    4. Assegna memoria su heap per object.
    5. Esegui costruttore.
    6. Stampa il valore di obj che è ancora null (poiché non fa parte delle variabili inizializzate dell’heap e del costruttore).
    7. Al termine del costruttore, eseguire il successivo inizializzatore statico che imposta obj su una nuova istanza di Object .
  4. Inizializzazione della class eseguita. Da questo punto, tutte le chiamate del costruttore si comporteranno come si presume / si aspetti – cioè obj non sarebbe null ma un riferimento a un’istanza Object .

Ricorda che Java specifica che una variabile final viene assegnata un valore una volta. Non è che sia garantito che venga assegnato un valore quando il codice lo fa riferimento a meno che non si accerti che il codice lo faccia riferimento dopo che è stato assegnato.

Questo non è un bug. Questo è il modo definito per gestire l’utilizzo della class durante la sua inizializzazione. Se così non fosse, allora la JVM andrebbe in un loop infinito. Vedere il passo # 3.3 (se la JVM non salta l’inizializzazione per una class che è in fase di inizializzazione, dovrebbe semplicemente inizializzarla – ciclo infinito).

Nota bene, tutto ciò avviene sullo stesso thread che fa riferimento per primo alla class. In secondo luogo, la JVM garantisce che l’inizializzazione verrà completata prima che qualsiasi altro thread possa utilizzare questa class.

Questo perché Java esegue la sezione statica nell’ordine in cui è dichiarata. Nel tuo caso, la sequenza è

  1. nuova MyClass
  2. nuovo object

Quando viene eseguito # 1, obj non è ancora inizializzato, quindi stampa nulla. Prova quanto segue e vedrai la differenza:

 class MyClass { private static final Object obj = new Object(); private static MyClass myClass = new MyClass(); public MyClass() { System.out.println(obj); // will print null once } } 

In generale, è meglio evitare un simile costrutto tutti insieme. Se stai provando a creare un singleton, ecco come dovrebbe apparire il frammento di codice:

 class MyClass { private static final MyClass myClass = new MyClass(); private Object obj = new Object(); private MyClass() { System.out.println(obj); // will print null once } } 

questo perché i campi statici sono inizializzati nello stesso ordine in cui sono definiti.

@Pyrolistical

dal momento che l’iniziale del primo campo statico myclass non è completamente costruito … il risultato che ottengo è

null null [email protected] null