2015-06-15 6 views
53

Quanto segue non si compila, dando un messaggio di 'illegale riferimento in avanti':Perché due programmi hanno errori di referenziazione in avanti mentre il terzo no?

class StaticInitialisation { 

    static 
    { 
     System.out.println("Test string is: " + testString); 
    } 

    private static String testString; 

    public static void main(String args[]) { 
     new StaticInitialisation(); 
    } 
} 

Tuttavia, il seguente fa compilare:

class InstanceInitialisation1 { 

    { 
     System.out.println("Test string is: " + this.testString); 
    } 

    private String testString; 

    public static void main(String args[]) { 
     new InstanceInitialisation1(); 
    } 
} 

Ma il seguente non si compila, dando un 'in avanti illegale messaggio di riferimento':

class InstanceInitialisation2 { 

     private String testString1; 

    { 
     testString1 = testString2; 
    } 

    private String testString2; 

    public static void main(String args[]) { 
     new InstanceInitialisation2(); 
    } 
} 

Perché StaticInitialisation e InstanceInitialisation2 si compila, mentre InstanceInitialisation1 doe S?

+1

Il terzo frammento avrebbe funzionato se è cambiata in 'testString1 = this.testString2;' – Eran

+1

E 'la prima volta che vedo un blocco di codice in una classe Java che non è parte di un ctor, o Ctor o metodo statico. piacerebbe sapere cosa è, come mai passa la compilazione e quando viene eseguito –

+12

@sharonbn: Sono inizializzatori di istanza o inizializzatori statici, descritti rispettivamente nelle sezioni 8.6 e 8.7 del JLS. –

risposta

57

Questa è coperto dalla sezione 8.3.3 della JLS:

L'utilizzo di variabili di classe le cui dichiarazioni comparire testualmente dopo l'uso a volte è limitato, anche se queste variabili di classe sono di portata (§6.3). Nello specifico, si tratta di un errore di compilazione se tutte le seguenti condizioni:

  • La dichiarazione di una variabile di classe in una classe o interfaccia C appare testualmente dopo un utilizzo della variabile di classe;

  • L'uso è un nome semplice in un inizializzatore di variabile di classe C o un inizializzatore statico di C;

  • L'utilizzo non è sul lato sinistro di un incarico;

  • C è la classe più interna o l'interfaccia che racchiude l'utilizzo.

uso di variabili istanza cui dichiarazioni comparire testualmente dopo l'uso è limitato a volte, anche se queste variabili istanza sono portata. Nello specifico, si tratta di un errore di compilazione se tutte le seguenti condizioni:

  • La dichiarazione di una variabile di istanza in una classe o interfaccia C appare testualmente dopo un uso della variabile di istanza;

  • L'uso è un nome semplice in un inizializzatore di variabile di istanza di C o un inizializzatore di istanza di C;

  • L'utilizzo non è sul lato sinistro di un incarico;

  • C è la classe più interna o l'interfaccia che racchiude l'utilizzo.

Nel suo secondo caso, l'uso non è un nome semplice - hai this esplicitamente. Ciò significa che non è conforme al secondo punto elenco nella seconda lista citata sopra, quindi non ci sono errori.

se si cambia a:

System.out.println("Test string is: " + testString); 

...quindi non verrà compilato.

O nella direzione opposta, è possibile modificare il codice nel blocco di inizializzazione statica a:

System.out.println("Test string is: " + StaticInitialisation.testString); 

Strano, ma questo è il modo in cui va.

+1

Sono venuto qui a causa del tuo tweet, e tu hai il mio +1, ma questo è [non nuovo] (http://stackoverflow.com/q/15820302/256431) come @ bayou.io collega a. –

+0

@MarkHurd: Sì, è solo una novità per me :) –

1

Qui ciò che dobbiamo capire è che nel 2 snippet di codice si sta utilizzando il blocco e questa parola chiave.

  1. Il blocco viene eseguito se viene creato oggetto.
  2. Ciò significa che l'oggetto viene creato nell'area dell'heap.
  3. Si utilizza esternamente questa parola chiave per ottenere un valore di variabile di istanza.
  4. Qui l'oggetto creato con valori predefiniti che verranno restituiti come valore.
  5. Se non si utilizza questa parola chiave, non è possibile compilare anche il secondo snippet.
2

Vediamo questi due esempi, immagino che questo ti renderà chiaro.

public class InstanceAndSataticInit { 

    { 
     System.out.println("Test string is (instance init): " + this.testString); 
    } 

    static{ 
     System.out.println("Test string is (static init): " + InstanceAndSataticInit.testStringStatic); 
    } 

    public static String testStringStatic="test"; 
    public String testString="test"; 

    public static void main(String args[]) { 
     new InstanceAndSataticInit(); 
    } 

} 

uscita:

Test string is (static init): null 
Test string is (instance init): null 

E

public class InstanceAndSataticInitVariableFirst { 

    public static String testStringStatic="test"; 
    public String testString="test"; 

    { 
     System.out.println("Test string is (instance init): " + this.testString); 
    } 

    static{ 
     System.out.println("Test string is (static init): " + InstanceAndSataticInitVariableFirst.testStringStatic); 
    } 



    public static void main(String args[]) { 
     new InstanceAndSataticInitVariableFirst(); 
    } 


} 

uscita:

Test string is (static init): test 
Test string is (instance init): test 

Così si può dire della sequenza sono come questo.

  1. La variabile statica verrà creata ma non inizializzata.

  2. L'inizializzazione statica verrà eseguita in base alla sequenza indicata.

  3. La variabile non statica verrà creata ma non inizializzata.
  4. L'inizializzazione non statica verrà eseguita in base alla sequenza indicata.

In sequenza intendo l'aspetto nel codice.

Credo che questo passi rispondere alla tua due non lavora esempio StaticInitialisation e InstanceInitialisation2

Ma nel caso in cui il secondo esempio di lavoroInstanceInitialisation1 utilizzando this parola chiave in realtà si sta aiutando compilatore di trascurare la gerarchia testuale.La stessa cosa accade in caso di static quando chiamo InstanceAndSataticInit.testStringStatic nel mio primo esempio InstanceAndSataticInit

2

semplice ragione - è troppo costoso o impossibile analizzare e vietare tutti i riferimenti in avanti. per esempio.

{ 
    print(getX(); ); // this.x 
    print(that().x); // this.x 
} 

int x; 
int getX(){ return x; } 

This that(){ return this; } 

Le specifiche si limitano a vietare alcuni casi semplici indicativi di errori comuni del programmatore.

Vedi anche Recursive initializer works when I add "this"?

Problemi correlati