Blocco statico in Java non eseguito


87
class Test {
    public static void main(String arg[]) {    
        System.out.println("**MAIN METHOD");
        System.out.println(Mno.VAL); // SOP(9090);
        System.out.println(Mno.VAL + 100); // SOP(9190);
    }

}

class Mno {
    final static int VAL = 9090;
    static {
        System.out.println("**STATIC BLOCK OF Mno\t: " + VAL);
    }
}

So che un staticblocco viene eseguito quando viene caricata la classe. Ma in questo caso la variabile di istanza all'interno della classe Mnoè final, per questo motivo il staticblocco non è in esecuzione.

Perchè è così? E se dovessi rimuovere il final, funzionerebbe bene?

Quale memoria verrà allocata per prima, la static finalvariabile o il staticblocco?

Se a causa del finalmodificatore di accesso la classe non viene caricata, come può la variabile ottenere memoria?


1
Qual è l'errore e il messaggio esatti che ricevi?
Patashu

@ Patashu, non ci sono errori, è un dubbio
Sthita

Risposte:


134
  1. Un static final intcampo è una costante in fase di compilazione e il suo valore è codificato nella classe di destinazione senza un riferimento alla sua origine;
  2. quindi la tua classe principale non attiva il caricamento della classe contenente il campo;
  3. pertanto l'inizializzatore statico in quella classe non viene eseguito.

Nello specifico, il bytecode compilato corrisponde a questo:

public static void main(String arg[]){    
    System.out.println("**MAIN METHOD");
    System.out.println(9090)
    System.out.println(9190)
}

Non appena rimuovi final, non è più una costante del tempo di compilazione e il comportamento speciale descritto sopra non si applica. La Mnoclasse viene caricata come previsto e il suo inizializzatore statico viene eseguito.


1
Ma allora come viene valutato il valore della variabile finale nella classe senza caricare la classe?
Sumit Desai

18
Tutta la valutazione avviene in fase di compilazione e il risultato finale è codificato in tutte le posizioni che fanno riferimento alla variabile.
Marko Topolnik

1
Quindi, se invece di una variabile primitiva, è un oggetto, allora tale codifica non sarà possibile. Non è vero? Quindi, in quel caso, quella classe verrà caricata e il blocco statico verrà eseguito?
Sumit Desai

2
Marko, il dubbio di Sumit è giusto anche se invece di primitivo, è un oggetto, allora tale codifica non sarà possibile. Non è vero? Quindi, in quel caso, quella classe verrà caricata e il blocco statico verrà eseguito?
Sthita

8
@SumitDesai Esattamente, funziona solo per valori primitivi e stringhe letterali. Per tutti i dettagli leggere il capitolo pertinente nelle specifiche del linguaggio Java
Marko Topolnik

8

Il motivo per cui la classe non viene caricata è che VALè final AND è inizializzata con un'espressione costante (9090). Se, e solo se, queste due condizioni sono soddisfatte, la costante viene valutata in fase di compilazione e "hardcoded" dove necessario.

Per evitare che l'espressione venga valutata in fase di compilazione (e per fare in modo che la JVM carichi la tua classe), puoi:

  • rimuovi la parola chiave finale:

    static int VAL = 9090; //not a constant variable any more
    
  • o cambia l'espressione a destra in qualcosa di non costante (anche se la variabile è ancora finale):

    final static int VAL = getInt(); //not a constant expression any more
    static int getInt() { return 9090; }
    

5

Se vedi il bytecode generato usando javap -v Test.class, main () viene fuori come:

public static void main(java.lang.String[]) throws java.lang.Exception;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String **MAIN METHOD
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: sipush        9090
        14: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        20: sipush        9190
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: return        

È possibile vedere chiaramente in " 11: sipush 9090" che il valore finale statico viene utilizzato direttamente, perché Mno.VAL è una costante del tempo di compilazione. Pertanto non è necessario caricare la classe Mno. Quindi il blocco statico di Mno non viene eseguito.

È possibile eseguire il blocco statico caricando manualmente Mno come di seguito:

class Test{
    public static void main(String arg[]) throws Exception {
        System.out.println("**MAIN METHOD");
        Class.forName("Mno");                 // Load Mno
        System.out.println(Mno.VAL);
        System.out.println(Mno.VAL+100);
    }

}

class Mno{
    final static int VAL=9090;
    static{
        System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
    }
}

1
  1. In realtà non hai esteso quella classe Mno, quindi all'avvio della compilazione genererà una costante della variabile VAL e quando inizierà l'esecuzione quando quella variabile è necessaria, il suo carico è dalla memoria. Quindi non è necessario che il riferimento alla classe in modo che il blocco statico non venga eseguito.

  2. se la classe Aestende la classe Mno, il blocco statico viene incluso nella classe, Ase lo fai, il blocco statico viene eseguito. Per esempio..

    public class A extends Mno {
    
        public static void main(String arg[]){    
            System.out.println("**MAIN METHOD");
            System.out.println(Mno.VAL);//SOP(9090);
            System.out.println(Mno.VAL+100);//SOP(9190);
        }
    
    }
    
    class Mno {
        final static int VAL=9090;
        static {
            System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
        }
    }
    

0

Per quanto ne so, verrà eseguito in ordine di apparizione. Per esempio :

 public class Statique {
     public static final String value1 = init1();

     static {
         System.out.println("trace middle");
     }
     public static final String value2 = init2();


     public static String init1() {
         System.out.println("trace init1");
         return "1";
     }
     public static String init2() {
         System.out.println("trace init2");
         return "2";
     }
 }

stamperà

  trace init1
  trace middle
  trace init2

L'ho appena testato e le statistiche vengono inizializzate (=> print) quando la classe "Statique" viene effettivamente utilizzata ed "eseguita" in un altro pezzo di codice (nel mio caso ho fatto "new Statique ()".


2
Stai ottenendo questo output perché stai caricando la Statiqueclasse facendo new Statique(). Mentre nella domanda posta, la Mnoclasse non viene caricata affatto.
RAS

@Fabyen, se sto creando un oggetto di Mno nella classe di test in questo modo: Mno anc = New Mno (); allora va bene, ma lo scenario attuale non lo sto facendo, il mio dubbio è che se sto rimuovendo final, il blocco statico viene eseguito correttamente altrimenti non viene eseguito, perché?
Sthita

1
Sì, la risposta di seguito è perfetta. Nel bytecode di Main.class (utilizzando Mno.VAL), 9090 si trova hard coded. Rimuovi final, compila, quindi usa javap Main, vedrai getstatic # 16; // Campo Statique.VAL: I . Rimetti finale, compila, quindi usa javap Main, vedrai sipush 9090 .
Fabyen

1
Poiché è hardcoded in Main.class, non c'è motivo di caricare la classe MNO, quindi non c'è inizializzazione statica.
Fabyen

Questo risponde alla seconda domanda: "Quale memoria verrà allocata per prima, la variabile finale statica o il blocco statico?" (ordine lessicale)
Hauke ​​Ingmar Schmidt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.