Arbeiten mit statischen Konstruktoren in Java

11

Ich habe kein vollständiges Verständnis für statische Konstruktoren in Java erhalten. Wenn es erlaubt ist, warum ist es erlaubt? In welchen Szenarien würden Sie es verwenden? Welchen Zweck würde es erfüllen? Kann mir bitte jemand ein einfaches Beispiel geben?

Kapeel45
quelle
2
Das Teilen Ihrer Forschung hilft allen. Sagen Sie uns, was Sie versucht haben und warum es Ihren Anforderungen nicht entsprach. Dies zeigt, dass Sie sich die Zeit genommen haben, um sich selbst zu helfen. Es erspart uns, offensichtliche Antworten zu wiederholen, und vor allem hilft es Ihnen, eine spezifischere und relevantere Antwort zu erhalten. Siehe auch Fragen
Mücke
3
Es gibt keinen "statischen Konstruktor".
David Conrad

Antworten:

27

Streng genommen Java hat keine statischen Konstruktoren , weil ein Konstruktor per definitionem nicht sein statisch. Was Sie meinen, wird als "statischer Initialisierungsblock" bezeichnet. Ein Konstruktor impliziert, dass Sie ein Objekt konstruieren. Sie können keinen Konstruktor für eine Klasse haben, da eine Klasse keine Instanz von sich selbst ist. Es ist einfach eine Klasse. Das, was die Klasse "konstruiert", heißt Compiler (oder virtuelle Maschine, je nachdem, was unter "Konstrukten" zu verstehen ist). Wenn Sie Code innerhalb eines anderen Codes erstellen, beginnen Sie mit der Codegenerierung ein ganz anderes Tier.

Abgesehen von der Nichtauswahl wird ein statischer Initialisierungsblock verwendet, um komplexe statische Felder (oder Felder auf Klassenebene) für eine Klasse zu initialisieren. Normalerweise werden diese verwendet, um Dinge zu initialisieren, die entweder nicht in einer Zeile initialisiert werden können oder die erfordern, dass zuerst ein anderes Objekt (das möglicherweise in der Klasse ist, in der der statische Block implementiert ist oder nicht) initialisiert wird.

Grundsätzlich könnte man sie verwenden, um der Klasse zu sagen: "Hey, setze die Variable A ZUERST auf diesen Wert und verwende dann, sobald dies erledigt ist, den Wert von A, um B zu initialisieren." Da Java erfordert, dass die Standardfeldinitialisierung entweder innerhalb eines Konstruktors oder einer Methode oder über den Aufruf eines Konstruktors oder einer Methode (sofern es sich nicht um ein Literal handelt) erfolgt, können diese eine bequeme Methode zum Initialisieren komplexer statischer Objekte sein.

Statische Initialisierungsblöcke werden nicht allzu oft benötigt und sollten im Allgemeinen vermieden werden, es sei denn, sie haben eine echte Verwendung. Versteh mich nicht falsch, sie haben ihren Platz in Java, aber wie viele andere Dinge (wie break-, return-, switch- und goto-Anweisungen) können sie leicht überbeansprucht werden, was ihre Lesbarkeit und Wartbarkeit des Codes verringert -Basis, in der sie verwendet werden.

Ein kurzes Beispiel für die Verwendung eines statischen Initialisierungsblocks wäre das Folgende (gemäß der hervorragenden Erklärung der hier gefundenen statischen Initialisierungsblöcke ):

Code:

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");
    }
}    

Ausgabe:

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

Einige Fälle, in denen statische Blöcke nützlich sein können:

  • Wenn Sie Treiber und andere Elemente in den Namespace laden. Zum Beispiel hat die Klassenklasse einen statischen Block, in dem sie die Eingeborenen registriert.
  • Wenn Sie eine Berechnung durchführen müssen, um Ihre statischen Variablen zu initialisieren, können Sie einen statischen Block deklarieren, der beim ersten Laden der Klasse genau einmal ausgeführt wird.
  • Sicherheitsprobleme oder Protokollierungsaufgaben

Einige Gründe, statische Blöcke NICHT zu verwenden (in anderen Situationen):

  • Es gibt eine Einschränkung von JVM, dass ein statischer Initialisierungsblock 64 KB nicht überschreiten darf.
  • Sie können keine geprüften Ausnahmen auslösen.
  • Sie können das thisSchlüsselwort nicht verwenden, da keine Instanz vorhanden ist.
  • Sie sollten nicht versuchen, auf Super zuzugreifen, da es für statische Blöcke so etwas nicht gibt.
  • Sie sollten nichts von diesem Block zurückgeben.
  • Statische Blöcke machen das Testen zu einem Albtraum.

Ich sollte beachten: Während einige Sprachen (wie C #) möglicherweise eine Syntax für "Konstruktoren" haben, die statisch sind, funktionieren diese "Konstruktoren" ähnlich wie statische Initialisierungsblöcke in Java und werden von vielen (einschließlich mir) als angesehen Fehlbezeichnungen in der Sprache angesichts des Grundkonzepts eines OOP-Konstruktors .

Gurgadurgen
quelle
Der Link zu statischen Blöcken funktioniert nicht mehr
ASh
5

Es wird verwendet, um Felder zu initialisieren, die schwieriger sind, als sie einfach zuzuweisen:

public class Example{

    public final static Map<String, String> preFilledField;

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

Es ist nicht möglich, eine Karte bei der Initialisierung zu füllen (es sei denn, Sie verwenden den anonymen Unterklassen-Hack). Dies ist daher der beste Weg, um sicherzustellen, dass sie vor der ersten Verwendung gefüllt wird

Sie können dies auch tun, um beim Initialisieren aktivierte Ausnahmen abzufangen

Ratschenfreak
quelle
Dies war eine der Hauptanwendungen, die ich gesehen habe, aber mit der Einbeziehung der statischen Factory-Methoden für das Sammlungsframework in Java 9 wurde dieser Anwendungsfall in gewisser Weise wirklich veraltet.
Hangman4358
2

Gurgadurgens Antwort ist wahrscheinlich das, wonach Sie suchen, aber ich füge nur ein paar andere Punkte hinzu, die manchmal vernachlässigt werden, wenn jemand einen "statischen Konstruktor" will.

Wenn Sie eine statische Methode wünschen, die eine Instanz Ihrer Klasse erstellt, können Sie eine statische Methode erstellen, die einfach den Konstruktor der Klasse aufruft.

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() ) ;
    }
}

Dies erzeugt die Ausgabe:

stuff
more stuff
even more stuff

Der andere Grund, eine statische Methode zum Erstellen einer Instanz zu erstellen, ist der Fall, wenn Sie sicherstellen möchten, dass zu einem bestimmten Zeitpunkt genau eine Instanz Ihrer Klasse vorhanden ist. Dies wird als Singleton bezeichnet . Konventionell würde eine solche Klasse eine statische Methode bereitstellen, die aufgerufen wird getInstance(), um die einzige Instanz zu erhalten , die als "Singleton" behandelt wird.

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() ) ;
    }
}

Dies erzeugt Folgendes.

stuff
stuff
other stuff
changed stuff

Wenn Sie einen Verweis auf den Singleton speichern und dann ändern, wird beim nächsten Aufruf getInstance()dieser geänderte Singleton abgerufen.

Es ist verlockend, Singletons zu verwenden, um globale Variablen für Ihre Anwendung zu erstellen. In einigen Kontexten kann dies nützlich sein, aber es kann Sie auch in Schwierigkeiten bringen. Insbesondere bei der Entwicklung einer Android-App stieß ich auf einen interessanten Fehler, bei dem Singleton-Instanzen verloren gehen konnten. Das Springen von einer Aktivität zu einer anderen kann manchmal dazu führen, dass die JVM einen neuen "Klassenlader" verwendet, der nichts über den Singleton weiß, der von einem vorherigen Klassenlader statisch gespeichert wurde.

Nullbandbreite
quelle
0

Obwohl ich verstehe, dass es viele gibt, die den Begriff eines "statischen Konstruktors" als Fehlbezeichnung ansehen, glaube ich nicht, dass dies der Fall ist. Das Problem besteht darin, beide Klassen und ihre Instanzobjekte zu erstellen . In anderen Threads wurde angegeben, dass die Konstruktion der Klasse die Aufgabe des Compilers ist. Selbst in Java ist dies nur zur Hälfte der Fall.

Der Compiler erstellt für jede Klassendefinition ein Gerüst. Das Gerüst enthält Metadaten über die Klasse und welche Instanzen zum Zeitpunkt der Erstellung enthalten sein sollten. Wenn eine Klasse ein Feld definiert, dem ein konstanter Grundwert zugewiesen ist, wird dieser Wert vom Compiler in das Gerüst aufgenommen. Für alle anderen zugewiesenen Werttypen generiert der Compiler eine Initialisierungsroutine, die vor der Erstellung der Instanz der ersten Klasse einmal ausgeführt werden soll und das Gerüst mit den richtigen Werten aktualisiert. Dieses Update kann vom Compiler nicht durchgeführt werden.

Da diese einmalige Aktualisierung des Gerüsts häufig eine wichtige Voraussetzung für das ordnungsgemäße Funktionieren der Instanzkonstruktoren ist, ist es auch sinnvoll, sie als Konstruktortyp zu bezeichnen. Aus diesem Grund wird es in gängigen OO-Sprachen, die das Konzept unterstützen, als statischer Konstruktor bezeichnet. Das Konzept hinter den statischen Initialisierungsblöcken in Java ist kaum mehr als eine semantische Änderung, um der Vorstellung zu entsprechen, dass ein Java-Programmierer sowohl system- als auch implementierungsunabhängig sein sollte.

Arkain
quelle
0

Wie andere Antworten bereits sagten, können Sie statische Methoden schreiben, die ein Objekt erstellen. Dies umgeht das Fehlen benannter Konstruktoren in Java (und vielen anderen Sprachen. Delphi unterstützt benannte Konstruktoren). Sie können mit den Typen und der Reihenfolge der Parameter spielen, dies kann jedoch zu unklarem und fragilem Code führen.

Zum Beispiel könnten wir ein Szenario erfinden, in dem Ihr Objekt aus einer XML-Zeichenfolge oder einer JSON-Zeichenfolge erstellt werden kann. Sie könnten Methoden schreiben wie:

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

Ich habe dies sehr gelegentlich als Alternative zu einem parameterlosen Konstruktor mit benannten Initialisierungsmethoden verwendet:

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

Sie können eine statische Erstellungsmethode als Implementierung des Builder-Musters in Ihrer Klasse betrachten.

Kiwiron
quelle
Beim Lesen der Frage geht es um statische Initialisierer, nicht um normale statische Methoden.
CodesInChaos
-2

Sie können den Abschnitt "statisch" wie einen Konstruktor auf Klassenebene anzeigen, mit dem Klasseneigenschaften initialisiert werden (statisch in Java). Das gleiche wie der "normale" Konstruktor, der zum Initialisieren von Eigenschaften auf Instanzebene verwendet wird.

AlfredoCasado
quelle