Lavorare con il costruttore statico in Java


11

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


2
Condividere la tua ricerca aiuta tutti. Raccontaci cosa hai provato e perché non ha soddisfatto le tue esigenze. Ciò dimostra che hai impiegato del tempo per cercare di aiutarti, ci salva dal ribadire risposte ovvie e soprattutto ti aiuta a ottenere una risposta più specifica e pertinente. Vedi anche Come chiedere
moscerino

3
Non esiste un "costruttore statico".
David Conrad,

Risposte:


28

A rigor di termini, Java non ha costruttori statici perché un costruttore, per definizione, non può essere statico. Quello a cui ti riferisci è chiamato "blocco di inizializzazione statica". 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 lezione. La cosa che "costruisce" la classe si chiama compilatore (o macchina virtuale, a seconda di cosa si intende per "costrutti"), e se inizi a costruire codice all'interno di un altro codice, stai entrando nella generazione di codice, che è una bestia completamente diversa.

A parte il nit-picking, un blocco di inizializzazione statica viene utilizzato per inizializzare campi statici complessi (o di livello classe) per una classe. Solitamente vengono utilizzati per inizializzare cose che non possono essere inizializzate in una riga o richiedono che venga inizializzato per primo un altro oggetto (che può o meno essere nella classe in cui è implementato il blocco statico).

Fondamentalmente, si potrebbero usare per dire alla classe "Ehi, imposta la variabile A su questo valore PRIMA, 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 un valore letterale), questi possono essere un metodo conveniente per inizializzare oggetti complessi e statici.

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

Un breve esempio di un blocco di inizializzazione statica utilizzato sarebbe il seguente (come per l' eccellente spiegazione dei blocchi di inizializzazione statica che si trova 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");
    }
}    

Produzione:

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 è necessario eseguire il calcolo per inizializzare le variabili statiche, è possibile 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à relative alla registrazione

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

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

Dovrei notare: mentre alcuni linguaggi (come C #) possono avere una 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 incluso) come impropri nella lingua, dato il concetto di base di un costruttore OOP .


il link sui blocchi statici non funziona più
ASh

5

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

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 riempire una mappa durante l'inizializzazione (a meno che non si usi l'hack anonimo della sottoclasse) quindi questo è il modo migliore per garantire che sia riempito prima del primo utilizzo

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


Questo è stato uno degli usi principali che ho visto, ma con l'inclusione dei metodi factory statici per il framework di raccolte in Java 9 questo caso d'uso è stato in qualche modo deprecato.
Hangman4358,

2

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

Se desideri un metodo statico che crei un'istanza della tua classe, puoi creare un metodo statico che invoca 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 esista esattamente un'istanza della propria classe in un dato momento; questo si chiama singleton . Per convenzione, una tale classe fornirebbe un metodo statico chiamato getInstance()per ottenere l'unica e unica istanza che viene 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 chiamata successiva per getInstance()ottenere quel singleton modificato.

È allettante utilizzare 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, ho riscontrato un bug interessante durante lo sviluppo di un'app Android, in cui le istanze singleton potevano andare perse. Saltare da un'attività all'altra a volte può portare la JVM a usare un nuovo "caricatore di classi" che non conoscerà il singleton che è stato memorizzato staticamente da un precedente caricatore di classi.


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 è in procinto di costruire entrambe le classi e i loro oggetti di istanza . È stato affermato in altri thread che la costruzione della classe è il lavoro del compilatore. Anche in Java, questo è solo per metà vero.

Il compilatore costruisce un'impalcatura per ogni definizione di classe. L'impalcatura contiene metadati sulla classe e su quali istanze dovrebbero avere al momento della costruzione. Se una classe definisce un campo a cui è assegnato un valore primitivo costante, quel compilatore lo include nello scaffold. Per qualsiasi altro tipo di valore assegnato, il compilatore genera una routine di inizializzazione che deve essere eseguita 1 volta, prima della creazione dell'istanza di prima classe, che aggiorna lo scaffold con i valori corretti. Questo aggiornamento non può essere eseguito dal compilatore.

Poiché questo aggiornamento una tantum dell'impalcatura è spesso un prerequisito fondamentale per il corretto funzionamento dei costruttori di istanze, è anche ragionevole chiamarlo un tipo di costruttore. Questo è il motivo per cui nei linguaggi OO comuni che supportano il concetto, viene chiamato costruttore statico. Il concetto alla base dei blocchi di inizializzazione statica in Java è poco più di un cambiamento semantico per rimanere in linea con l'idea che un programmatore Java dovrebbe essere sia agnostico di sistema che di implementazione.


0

Come hanno già detto altre risposte, puoi scrivere metodi statici che costruiscono un oggetto. Questo aggira la mancanza di costruttori nominati in Java (e in molte altre lingue. Delphi supporta costruttori nominati). Puoi giocare con i tipi e l'ordine dei parametri, ma questo può portare a codici poco chiari e fragili.

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

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

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

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

È possibile considerare un metodo di creazione statica come implementazione del modello builder all'interno della classe.


Mentre leggo la domanda, si tratta di inizializzatori statici, non di normali emthods statici.
Codici A Caos il

-2

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

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.