Qual è la differenza tra un blocco di codice di inizializzazione statico e non statico


357

La mia domanda riguarda un uso particolare della parola chiave statica. È possibile utilizzare la staticparola chiave per coprire un blocco di codice all'interno di una classe che non appartiene a nessuna funzione. Ad esempio le seguenti compilazioni di codice:

public class Test {
    private static final int a;    
    static {
        a = 5;
        doSomething(a);
    }
    private static int doSomething(int x) {
        return (x+5);
    }
}

Se rimuovi la staticparola chiave, si lamenta perché la variabile aè final. Tuttavia è possibile rimuovere entrambi finale staticle parole chiave e renderlo compilazione.

È confuso per me in entrambi i modi. Come posso avere una sezione di codice che non appartiene a nessun metodo? Come è possibile invocarlo? In generale, qual è lo scopo di questo utilizzo? O meglio, dove posso trovare la documentazione al riguardo?

Risposte:


403

Il blocco di codice con il modificatore statico indica un inizializzatore di classe ; senza il modificatore statico il blocco di codice è un inizializzatore di istanza .

Gli inizializzatori di classe vengono eseguiti nell'ordine in cui sono definiti (dall'alto in basso, proprio come i semplici inizializzatori di variabili) quando la classe viene caricata (in realtà, quando viene risolta, ma questo è un tecnicismo).

Gli inizializzatori dell'istanza vengono eseguiti nell'ordine definito quando la classe viene istanziata, immediatamente prima dell'esecuzione del codice del costruttore, immediatamente dopo l'invocazione del super costruttore.

Se rimuovi staticda int a, diventa una variabile di istanza, a cui non puoi accedere dal blocco di inizializzatore statico. Questo non verrà compilato con l'errore "non è possibile fare riferimento a una variabile non statica a da un contesto statico".

Se rimuovi anche staticdal blocco di inizializzazione, questo diventa un inizializzatore di istanza e quindi int aviene inizializzato durante la costruzione.


L'inizializzatore statico viene effettivamente richiamato in seguito, quando la classe viene inizializzata, dopo che è stata caricata e collegata. Ciò accade quando si crea un'istanza di un oggetto di una classe o si accede a una variabile o metodo statico sulla classe. In effetti se hai una classe con un inizializzatore statico e un metodo public static void staticMethod(){}, se esegui TestStatic.class.getMethod("staticMethod");. L'inizializzatore statico non verrà richiamato. Maggiori informazioni qui docs.oracle.com/javase/specs/jvms/se10/html/…
Totò,

@ Totò: Sì, questo è ciò che comporta la risoluzione della classe (almeno si riferivano ad essa come link + init come "risoluzione" molto tempo fa). Non mi sorprende che tu possa usare la riflessione per scoprire cose su una lezione senza che si risolva.
Lawrence Dol,

166

Uff! che cos'è l'inizializzatore statico?

L'inizializzatore statico è un static {}blocco di codice all'interno della classe java ed eseguito una sola volta prima che venga chiamato il costruttore o il metodo principale.

OK! Dimmi di più...

  • è un blocco di codice static { ... }all'interno di qualsiasi classe java. ed eseguito dalla macchina virtuale quando viene chiamata la classe.
  • Non returnsono supportate dichiarazioni.
  • Nessun argomento supportato.
  • No thiso supersono supportati.

Hmm dove posso usarlo?

Può essere usato ovunque ti senti bene :) così semplice. Ma vedo la maggior parte delle volte che viene utilizzato quando si esegue una connessione al database, API init, Logging ecc.

Non abbaiare! dov'è l'esempio?

package com.example.learnjava;

import java.util.ArrayList;

public class Fruit {

    static {
        System.out.println("Inside Static Initializer.");

        // fruits array
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Pear");

        // print fruits
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        System.out.println("End Static Initializer.\n");
    }

    public static void main(String[] args) {
        System.out.println("Inside Main Method.");
    }
}

Produzione???

Inizializzatore statico interno.

Mela

arancia

Pera

Inizializzatore statico finale.

Metodo principale interno.

Spero che sia di aiuto!


Grazie Madan! Blocco statico può essere usato al posto di afterPropertiesSet()di InitializingBean?
Alexander Suraphel,

3
Si, puoi! L'inizializzatore statico viene chiamato quando la classe viene caricata da jvm. Quindi è la prima vera fase in cui il codice viene eseguito. Se anche tu hai un costruttore, l'ordine sarebbe: inizializzatore statico, costruttore, afterPropertiesSet
Martin Baumgartner

57

Il staticblocco è un "inizializzatore statico".

Viene richiamato automaticamente quando la classe viene caricata e non c'è altro modo per invocarla (nemmeno tramite Reflection).

Personalmente l'ho usato solo quando ho scritto il codice JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}

6
No, nessun modo esplicito per invocarlo, l'inizializzatore di classe non è mai rappresentato da Methodun'istanza ma solo invocato dalla macchina virtuale Java.
Rafael Winterhalter,

46

Questo è direttamente da http://www.programcreek.com/2011/10/java-class-instance-initializers/

1. Ordine di esecuzione

Guarda la seguente classe, sai quale viene eseguito per primo?

public class Foo {

    //instance variable initializer
    String s = "abc";

    //constructor
    public Foo() {
        System.out.println("constructor called");
    }

    //static initializer
    static {
        System.out.println("static initializer called");
    }

    //instance initializer
    {
        System.out.println("instance initializer called");
    }

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

Produzione:

inizializzatore statico chiamato

inizializzatore dell'istanza chiamato

chiamato dal costruttore

inizializzatore dell'istanza chiamato

chiamato dal costruttore

2. Come funzionano gli inizializzatori dell'istanza Java?

L'inizializzatore dell'istanza sopra contiene un'istruzione println. Per capire come funziona, possiamo considerarlo come un'istruzione di assegnazione variabile, ad es b = 0. Questo può rendere più ovvio capire.

Invece di

int b = 0, potresti scrivere

int b;
b = 0;

Pertanto, gli inizializzatori di istanza e gli inizializzatori di variabili di istanza sono praticamente gli stessi.

3. Quando sono utili gli inizializzatori di istanza?

L'uso degli inizializzatori di istanza è raro, ma può comunque essere una valida alternativa agli inizializzatori di variabili di istanza se:

  1. Il codice dell'inizializzatore deve gestire le eccezioni
  2. Eseguire calcoli che non possono essere espressi con un inizializzatore di variabili di istanza.

Naturalmente, tale codice potrebbe essere scritto nei costruttori. Ma se una classe avesse più costruttori, dovresti ripetere il codice in ciascun costruttore.

Con un inizializzatore di istanza, puoi semplicemente scrivere il codice una volta e verrà eseguito indipendentemente dal costruttore utilizzato per creare l'oggetto. (Immagino che questo sia solo un concetto e non viene usato spesso.)

Un altro caso in cui gli inizializzatori di istanze sono utili sono le classi interne anonime, che non sono in grado di dichiarare alcun costruttore. (Sarà un buon posto per posizionare una funzione di registrazione?)

Grazie a Derhein.

Si noti inoltre che le classi anonime che implementano le interfacce [1] non hanno costruttori. Pertanto sono necessari inizializzatori di istanze per eseguire qualsiasi tipo di espressione in fase di costruzione.


12

"final" garantisce che una variabile deve essere inizializzata prima della fine del codice di inizializzazione dell'oggetto. Allo stesso modo, "static static" garantisce che una variabile verrà inizializzata dal codice di inizializzazione di fine classe. Omettere lo "statico" dal codice di inizializzazione lo trasforma in codice di inizializzazione dell'oggetto; quindi la tua variabile non soddisfa più le sue garanzie.


8

Non scrivere il codice in un blocco statico che deve essere invocato in qualsiasi punto del programma. Se lo scopo del codice deve essere invocato, è necessario inserirlo in un metodo.

È possibile scrivere blocchi di inizializzatori statici per inizializzare le variabili statiche quando la classe viene caricata ma questo codice può essere più complesso.

Un blocco di inizializzatore statico sembra un metodo senza nome, senza argomenti e senza tipo restituito. Dal momento che non lo chiami mai non ha bisogno di un nome. L'unica volta che viene chiamato è quando la macchina virtuale carica la classe.


6

quando uno sviluppatore utilizza un blocco di inizializzatore, il compilatore Java copia l'inizializzatore in ciascun costruttore della classe corrente.

Esempio:

il seguente codice:

class MyClass {

    private int myField = 3;
    {
        myField = myField + 2;
        //myField is worth 5 for all instance
    }

    public MyClass() {
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

è equivalente a:

class MyClass {

    private int myField = 3;

    public MyClass() {
        myField = myField + 2;
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        myField = myField + 2;
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

Spero che il mio esempio sia compreso dagli sviluppatori.


4

Il blocco di codice statico può essere utilizzato per istanziare o inizializzare le variabili di classe (al contrario delle variabili di oggetto). Quindi dichiarare "a" statico significa che è solo uno condiviso da tutti gli oggetti Test e il blocco di codice statico inizializza "a" solo una volta, quando la classe Test viene caricata per la prima volta, indipendentemente dal numero di oggetti Test creati.


Come follow-up, se non creo un'istanza dell'oggetto ma invece chiamo una funzione statica pubblica. Implica che questo blocco sia garantito per essere eseguito prima di questa chiamata di funzione pubblica? Grazie.
Szere Dyeri,

Se chiami una funzione statica pubblica della classe, la classe deve essere caricata per prima, quindi sì, l'inizializzatore statico verrà eseguito per primo.
Paul Tomblin,

A meno che non sia stata l'inizializzazione di classe che (indirettamente) ha chiamato il codice che sta cercando di usarlo. IFYSWIM. Dipendenze circolari e tutto il resto.
Tom Hawtin - tackline il

1
@ Tom ha ragione: è possibile scrivere qualcosa in cui un inizializzatore statico chiama un metodo statico prima che venga chiamato un altro inizializzatore statico, ma la mia mente indietreggia al pensiero, quindi non l'ho mai considerato.
Paul Tomblin,
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.