Lavorare con il costruttore statico in Java

9

Non ho avuto una comprensione completa dei costruttori statici in Java. Se è permesso, perché è permesso? In quali scenari lo useresti? Quale scopo servirebbe? Qualcuno può darmi un semplice esempio per favore?

    
posta Kapeel45 08.02.2014 - 21:17
fonte

6 risposte

24

Strettamente parlando, Java non ha costruttori statici perché un costruttore, per definizione, non può essere statico. Quello a cui ti riferisci è chiamato "blocco di inizializzazione statico". Un costruttore implica che stai costruendo un oggetto. Non puoi avere un costruttore per una classe perché una classe non è un'istanza di se stessa. È semplicemente una classe. La cosa che "costruisce" la classe è chiamata il compilatore (o la macchina virtuale, a seconda di cosa si intende per "costrutti"), e se si inizia a costruire codice all'interno di un altro codice, si entra nella generazione del codice, che è una bestia completamente diversa.

A parte la selezione nit da parte, viene utilizzato un blocco di inizializzazione statico per inizializzare campi statici (o di livello classe) complessi per una classe. Di solito questi sono usati per inizializzare cose che non possono essere inizializzate in una riga, o richiedono che qualche altro oggetto (che può o non può essere nella classe in cui è implementato il blocco statico) sia inizializzato per primo.

Fondamentalmente, si potrebbe usarli per dire alla classe "Ehi, imposta la variabile A su questo valore FIRST, quindi, una volta fatto, usa il valore di A per inizializzare B." Poiché Java richiede che l'inizializzazione del campo standard sia eseguita all'interno di un costruttore o di un metodo, o tramite la chiamata di un costruttore o di un metodo (a meno che non sia letterale), questi possono essere un metodo conveniente per inizializzare oggetti statici complessi.

I blocchi di inizializzazione statici non sono necessari troppo spesso, e generalmente dovrebbero essere evitati a meno che non abbiano un uso reale . Non fraintendetemi, hanno il loro posto in Java, ma come molte altre cose (come break, return, switch e goto statement) possono essere facilmente sovrautilizzati, il che riduce la loro leggibilità e la manutenibilità del codice -base in cui sono usati.

Un breve esempio di blocco di inizializzazione statico utilizzato è il seguente (secondo la eccellente spiegazione dei blocchi di inizializzazione statici trovati qui ):

Codice:

public class StaticExample{
    static {
        System.out.println("This is first static block");
    }

    public StaticExample(){
        System.out.println("This is constructor");
    }

    public static String staticString = "Static Variable";

    static {
        System.out.println("This is second static block and "
                                                    + staticString);
    }

    public static void main(String[] args){
        StaticExample statEx = new StaticExample();
        StaticExample.staticMethod2();
    }

    static {
        staticMethod();
        System.out.println("This is third static block");
    }

    public static void staticMethod() {
        System.out.println("This is static method");
    }

    public static void staticMethod2() {
        System.out.println("This is static method2");
    }
}    

Output:

This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2

Alcune istanze che elencano quando i blocchi statici possono essere utili:

  • Se stai caricando driver e altri elementi nello spazio dei nomi. Ad esempio, la classe Class ha un blocco statico in cui registra i nativi.
  • Se hai bisogno di fare calcoli per inizializzare le tue variabili statiche, puoi dichiarare un blocco statico che viene eseguito esattamente una volta, quando la classe viene caricata per la prima volta.
  • Problemi relativi alla sicurezza o attività correlate alla registrazione

Alcuni motivi per NON usare i blocchi statici (in altre situazioni):

  • Esiste una limitazione di JVM che un blocco di inizializzatore statico non deve superare 64 KB.
  • Non puoi lanciare Eccezioni controllate
  • Non puoi utilizzare la parola chiave this poiché non esiste un'istanza.
  • Non dovresti provare ad accedere a super perché non esiste una cosa del genere per i blocchi statici.
  • Non dovresti restituire nulla da questo blocco.
  • I blocchi statici rendono il test un incubo.

Dovrei notare: mentre alcuni linguaggi (come C #) possono avere sintassi per "costruttori" che sono statici, quei "costruttori" funzionano allo stesso modo dei blocchi di inizializzazione statici in Java, e sono visti da molti (me stesso inclusi) come errori nella lingua, dato il concetto di base di un costruttore OOP .

    
risposta data 08.02.2014 - 22:20
fonte
5

viene utilizzato per inizializzare i campi più difficili dell'assegnazione semplice:

public class Example{

    public final static Map<String, String> preFilledField;

    static{
        Map<String, String> tmp = new HashMap<>();
        //fill map
        preFilledField = Collections.unmodifiableMap(tmp);
    }
}

non è possibile compilare una mappa sull'inizializzazione (a meno che non si usi la sottoclasse anonima), quindi questo è il modo migliore per garantire che sia riempito prima del primo utilizzo

Puoi anche farlo per rilevare le eccezioni controllate durante l'inizializzazione

    
risposta data 08.02.2014 - 21:40
fonte
2

La risposta di Gurgadurgen è probabilmente quella che stai cercando, ma aggiungerò solo un paio di altri punti che a volte vengono trascurati quando qualcuno vuole un "costruttore statico".

Se vuoi un metodo statico che crea un'istanza della tua classe, puoi creare un metodo statico che richiama semplicemente il costruttore della classe.

public class Example
{
    /** Static method to create an instance. */
    public static Example build()
    { return new Example() ; }

    /** A field of each instance. */
    private String stuff ;

    /** The class's actual constructor. */
    public Example()
    { stuff = new String() ; }

    public String getStuff()
    { return this.stuff ; }

    /**
     * Mutator for "stuff" property. By convention this returns "void"
     * but you might want to return the object itself, to support the
     * sort of chained invocations that are becoming trendy now. You'll
     * see the stylistic benefits of chaining later in this example.
     */
    public Example setStuff( String newStuff )
    {
        this.stuff = newStuff ;
        return this ;
    }
}

public class ExampleTest
{
    public static void main( String[] args )
    {
        // The usual instance model.
        Example first = new Example() ;
        System.out.println( first.setStuff("stuff").getStuff() ) ;

        // Using your static method to construct an instance:
        Example second = Example.build() ;
        System.out.println( second.setStuff("more stuff").getStuff() ) ;

        // Chaining all the invocations at once:
        System.out.println( Example.build().setStuff("even more stuff").getStuff() ) ;
    }
}

Questo produce l'output:

stuff
more stuff
even more stuff

L'altro motivo per creare un metodo statico per costruire un'istanza è il caso in cui si desidera assicurarsi che esattamente una istanza della classe esista in un dato momento; questo è chiamato singleton . Per convenzione, tale classe fornirebbe un metodo statico chiamato getInstance() per ottenere l'unica istanza trattata come un "singleton".

public class SingletonExample extends Example
{
    // Note: This extends my previous example, which has a "stuff"
    // property, and a trivial constructor.

    /** The singleton instance, statically initialized as null. */
    private static SingletonExample singleton = null ;

    /**
     * The static accessor for the singleton. If no instance exists,
     * then it will be created; otherwise, the one that already exists
     * will be returned.
     */
    public static SingletonExample getInstance()
    {
        if( singleton == null )
            singleton = new SingletonExample() ;
        return singleton ;
    }
}

public class SingletonExampleTest
{
    public static void main( String[] args )
    {
        System.out.println( SingletonExample.getInstance().setStuff("stuff").getStuff() ) ;

        // You could still create instances of this class normally if you want to.
        SingletonExample otherstuff = new SingletonExample() ;
        otherstuff.setStuff("other stuff") ;

        // But watch what happens to this.
        System.out.println( SingletonExample.getInstance().getStuff() ) ;
        System.out.println( otherstuff.getStuff() ) ;

        // Now we show what happens when you start modifying the singleton.
        SingletonExample theoneandonly = SingletonExample.getInstance() ;
        theoneandonly.setStuff("changed stuff") ;
        System.out.println( SingletonExample.getInstance().getStuff() ) ;
    }
}

Questo produce quanto segue.

stuff
stuff
other stuff
changed stuff

Memorizzando un riferimento al singleton e quindi modificandolo, la prossima chiamata a getInstance() ottiene quel singleton modificato.

Si sta tentando di usare i singleton come modo per iniziare a creare variabili globali per la propria applicazione. In alcuni contesti questo può essere utile, ma può anche metterti nei guai. In particolare, mi sono imbattuto in un bug interessante durante lo sviluppo di un'app Android, in cui le istanze Singleton potevano andare perse. Saltare da un'attività all'altra può a volte portare la JVM a utilizzare un nuovo "caricatore di classi" che non conoscerà il singleton che è stato memorizzato staticamente da un precedente caricatore di classe.

    
risposta data 24.12.2014 - 17:08
fonte
0

Mentre capisco che ci sono molti che vedono la nozione di "costruttore statico" come un termine improprio, non credo che sia così. Il problema è nel processo di costruzione di di entrambe le classi e dei relativi oggetti istanza . È stato affermato in altri thread che la costruzione della classe è il lavoro del compilatore. Anche in Java, questo è vero solo per metà.

Il compilatore costruisce uno scaffold per ogni definizione di classe. Lo scaffold contiene metadati relativi alla classe e quali istanze dovrebbero avere al loro interno al momento della costruzione. Se una classe definisce un campo a cui è assegnato un valore primitivo costante, tale valore viene incluso nello scaffold dal compilatore. Per ogni altro tipo di valore assegnato, il compilatore genera una routine di inizializzazione che deve essere eseguita 1 volta, prima della creazione della prima istanza di classe, che aggiorna lo scaffold con i valori corretti. Questo aggiornamento non può essere eseguito dal compilatore.

Poiché questo aggiornamento una tantum allo scaffold è spesso un prerequisito critico per il corretto funzionamento dei costruttori di istanze, è anche ragionevole chiamarlo un tipo di costruttore. Questo è il motivo per cui nei comuni linguaggi OO che supportano il concetto, viene chiamato un costruttore statico. Il concetto alla base dei blocchi di inizializzazione statici in Java è poco più di una modifica semantica per rimanere in linea con la nozione che un programmatore Java dovrebbe essere sia agevole che sistemico e implementabile.

    
risposta data 13.03.2015 - 19:42
fonte
0

Come hanno detto altre risposte, puoi scrivere metodi statici che costruiscono un oggetto. Ciò aggira la mancanza di costruttori denominati in Java (e in molti altri linguaggi) Delphi supporta i costruttori denominati). Puoi giocare con i tipi e l'ordine dei parametri, ma questo può portare a codice poco chiaro e fragile.

Ad esempio, potremmo inventare uno scenario in cui il tuo oggetto può essere costruito da una stringa XML o da una stringa JSON. Potresti scrivere metodi come:

static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);

L'ho usato occasionalmente come alternativa a un costruttore senza parametri con metodi di inizializzazione denominati:

MyObject myObject = new MyObject();
myObject.loadXml(xml). 

Potresti considerare un metodo di creazione statica come implementazione del modello di build all'interno della tua classe.

    
risposta data 14.03.2015 - 05:17
fonte
-2

È possibile visualizzare la sezione "statica" come un costruttore di livello di classe per inizializzare le proprietà della classe (static in java). Lo stesso del costruttore "normale" utilizzato per inizializzare le proprietà del livello di istanza.

    
risposta data 08.02.2014 - 22:08
fonte

Leggi altre domande sui tag