2015-04-07 18 views
7

Ho classe AbstractsAndInterfaces:strano comportamento delle variabili statiche

public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces(); 
private static final int DELTA = 5; 

private static int BASE = 7; 

private int x; 
public AbstractsAndInterfaces() 
{ 
    //System.out.println(BASE); 
    //System.out.println(DELTA); 
    x = BASE + DELTA; 
} 
public static int getBASE() 
{ 
    return BASE; 
} 

    /** 
* @param args the command line arguments 
*/ 
public static void main(String[] args) { 

    System.out.println(AbstractsAndInterfaces.instance.x); 
} 

Perché si stampa 5?

Perché la variabile BASE non è inizializzata?

+2

In sostanza: http://stackoverflow.com/questions/27859435/java-static-final-field-initialization-order – marvin82

risposta

10

Questo perché:

private static final int DELTA = 5; sarà una costante fase di compilazione. Quindi, 5 sarà già disponibile (inizializzato) quando la classe viene inizializzata (una volta caricata).

La prima riga che viene eseguito è: public static AbstractsAndInterfaces instance = new AbstractsAndInterfaces();

Così, ora si va a:

public AbstractsAndInterfaces() 
{ 
    //System.out.println(BASE); 
    //System.out.println(DELTA); 
    x = BASE + DELTA; // same as x= 0+5 i.e default value of fields + constant value 5 
} 

Nel codice precedente, DELTA sarà 5 ma BASE sarà 0 come è non final. La modifica di BASE in final cambia il risultato in 12.

EDIT:

codice di esempio per mostrare lo scenario del PO con codice di byte.

public class Sample { 
    static Sample s = new Sample(); 
    static final int x = 5; 
    static int y = 10; 

    public Sample() { 
     int z = x + y; 
     System.out.println(z); 
    } 

    public static void main(String[] args) { 

    } 

} 

Nel codice precedente, quando il programma è conduzione, l'uscita sarà 5 e non 10. Ora diamo un'occhiata al codice byte .

Il codice byte del costruttore si presenta così:

p

ublic Sample(); 
    descriptor:()V 
    flags: ACC_PUBLIC 
    Code: 
    stack=2, locals=2, args_size=1 
     0: aload_0 
     1: invokespecial #24     // Method java/lang/Object."<init> 
:()V 
     4: iconst_5  ------->   // Here 5 is used directly as it is a compile time constant 
     5: getstatic  #20 ------->  // Field y:I 
     8: iadd 
     9: istore_1 
     10: getstatic  #25     // Field java/lang/System.out:Ljav 
/io/PrintStream; 
     13: iload_1 
     14: invokevirtual #31     // Method java/io/PrintStream.prin 
ln:(I)V 
     17: return 

codice byte per il blocco init statica:

statica {}; descrittore:() V bandiere: ACC_STATIC Codice: pila = 2, la gente del posto = 0, args_size = 0 0: nuovo # 1 // classe Sample 3: DUP 4: invokespecial # 15 // metodo "" :() V 7: putstatiC# 18 // Field s: LSample; 10: 10 bipush 12: putstatiC# 20 // Campo y: I 15: tornare LineNumberTable: linea 3: 0 linea 5: 10 Linea 1: 15 LocalVariableTable: Inizia Lunghezza Slot Nome Firma

Se si controlla attentamente, x non viene inizializzato nella init statica della classe. Solo y viene impostato come è statico. Ma quando guardi il costruttore, vedrai che la costante 5 viene utilizzata direttamente (iconst_5) e inserita nello stack.

Ora, se si aggiunge il seguente codice nel main():

public static void main(String[] args) { 
    System.out.println(Sample.x); 
    System.out.println(Sample.y); 
} 

Il bytecode per main() saranno:

public static void main(java.lang.String[]); 
    descriptor: ([Ljava/lang/String;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=1, args_size=1 
     0: getstatic  #25     // Field java/lang/System.out:Ljav 
a/io/PrintStream; 
     3: iconst_5 -->      // No "x" but a constant value instead of "x" 
     4: invokevirtual #31     // Method java/io/PrintStream.prin 
tln:(I)V 
     7: getstatic  #25     // Field java/lang/System.out:Ljav 
a/io/PrintStream; 
     10: getstatic  #20     // Field y:I --> but "y" is being fetched 
     13: invokevirtual #31     // Method java/io/PrintStream.prin 
tln:(I)V 
     16: return 
     LineNumberTable: 
     line 13: 0 
     line 14: 7 
     line 15: 16 
     LocalVariableTable: 
     Start Length Slot Name Signature 
      0  17  0 args [Ljava/lang/String; 
} 

Anche in questo caso osservare che x viene usato come iconst_5 mentre y è essere preso/indirizzato indirettamente.

+0

l'ultima riga è errata cambialo !! – Prashant

+2

@Prashant - Ya .. Typo, è stato confuso con nomi di variabili .. Modificato ora :) – TheLostMind

+2

Non dovrebbero essere inizializzati tutti i campi prima che venga chiamato il costruttore? Questo è ciò che afferma [JLS] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4). Penso che sia degno di nota il motivo per cui 'BASE' è ancora non inizializzato. – Maroun