Frage

So habe ich an meine Java Fähigkeiten als in der letzten Zeit auffrischen und haben ein paar Bits der Funktionalität gefunden, dass ich nicht über die zuvor nicht kannte. Statische und Instanz Initializers sind zwei solche Techniken.

Meine Frage ist, wann würde man einen Initialisierer statt einschließlich des Codes in einem Konstruktor verwenden? Ich habe gedacht, von ein paar offensichtlichen Möglichkeiten:

  • statische / instance Initialisierer verwendet werden kann, den Wert „endgültigen“ Statisch / Instanzvariablen während eines Konstruktor kann nicht

  • einzustellen
  • statische Initialisierer kann den Wert der statischen Variablen in einer Klasse gesetzt werden, die effizienter sein sollte zu Beginn eine „if (someStaticVar == null) // Dinge zu tun“ Codeblock als mit jeder Konstruktor

Beiden Fälle annehmen, dass der Code, der diese Variablen setzen erforderlich ist komplexer als einfach „var = Wert“, wie sonst wäre es nicht scheint kein Grund zu sein, einen Initialisierer zu verwenden, anstatt einfach den Wert, wenn erklärt die variablen.

Während jedoch diese sind nicht trivial Gewinne (vor allem die Fähigkeit, eine endgültige Variable zu setzen), scheint es, dass eine eher begrenzte Anzahl von Situationen gibt, in denen ein Initialisierer verwendet werden soll.

Man kann sicherlich einen Initialisierer für eine Menge verwenden, was in einem Konstruktor geschehen ist, aber ich sehe nicht wirklich den Grund, dies zu tun. Selbst wenn alle Konstrukteure für eine Klasse teilen sich eine große Menge an Code, die Nutzung eines privaten initialize () Funktion scheint mehr Sinn für mich als einen Initialisierer verwenden, weil es Sie nicht sperren in diesen Code laufen haben, wenn ein neues Schreiben Konstruktor.

Bin ich etwas fehlt? Gibt es eine Reihe von anderen Situationen, in denen ein Initialisierer verwendet werden soll? Oder ist es wirklich nur ein eher begrenztes Werkzeug in ganz bestimmten Situationen verwendet werden?

War es hilfreich?

Lösung

Statische Initialisierungen sind als cletus erwähnt und ich benutze sie auf die gleiche Weise. Wenn Sie eine statische Variable, die initialisiert werden soll, wenn die Klasse geladen wird, dann eine statische Initialisierer ist der Weg zu gehen, vor allem, da es Ihnen eine komplexe Initialisierung erlaubt, zu tun und haben immer noch die statische Variable final werden. Dies ist ein großer Gewinn.

Ich finde "if (someStaticVar == null) // do stuff" chaotisch und fehleranfällig sein. Wenn es statisch initialisiert und final deklariert, dann vermeiden Sie die Möglichkeit, es null zu sein.

Aber ich bin verwirrt, wenn Sie sagen:

  

statisch / Instanz initializers verwendet werden kann, um den Wert von „final“ zu setzen   statisch / Instanzvariablen während eines Konstruktor kann nicht

Ich nehme an, Sie beide sagen:

  • statische Initialisierer verwendet werden können, um den Wert von „final“ statischen Variablen während eines Konstruktor kann nicht
  • einstellen
  • Instanz initializers verwendet werden kann, um den Wert von „final“ Instanzvariablen während eines Konstruktor kann nicht
  • einstellen

und Sie sind richtig auf dem ersten Punkt, falsch auf dem zweiten. Sie können zum Beispiel wie folgt vorgehen:

class MyClass {
    private final int counter;
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

Auch wenn eine Menge Code zwischen Konstrukteuren gemeinsam, eine der besten Möglichkeiten, um damit zu umgehen zu Kettenbauer ist, den Standardwert bereitstellt. Dies macht ist ziemlich klar, was getan wird:

class MyClass {
    private final int counter;
    public MyClass() {
        this(0);
    }
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

Andere Tipps

Anonyme innere Klassen können nicht einen Konstruktor haben (wie sie anonym sind), so dass sie eine ziemlich natürliche Ergänzung zum Beispiel initializers.

Ich verwende meist statische Initialisierer Blöcke für die endgültige statische Daten einrichten, insbesondere Sammlungen. Zum Beispiel:

public class Deck {
  private final static List<String> SUITS;

  static {
    List<String> list = new ArrayList<String>();
    list.add("Clubs");
    list.add("Spades");
    list.add("Hearts");
    list.add("Diamonds");
    SUITS = Collections.unmodifiableList(list);
  }

  ...
}

Jetzt kann dieses Beispiel mit einer einzigen Codezeile erfolgen:

private final static List<String> SUITS =
  Collections.unmodifiableList(
    Arrays.asList("Clubs", "Spades", "Hearts", "Diamonds")
  );

aber die statische Version kann weit sauberere, vor allem, wenn die Einzelteile sind nicht-trivial zu initialisieren.

Eine naive Implementierung kann auch eine unveränderbare Liste nicht schaffen, die ein potenzieller Fehler. Die oben schafft eine unveränderliche Datenstruktur, die Sie gerne von öffentlichen Methoden zurückkehren können und so weiter.

Um nur einige bereits ausgezeichnete Punkte hier hinzufügen. Der statische Initialisierer ist sicher fädeln. Es wird ausgeführt, wenn die Klasse geladen wird, und macht somit eine einfachere statische Daten Initialisierung als einen Konstruktor verwenden, in dem Sie einen synchronisierten Block müßten zu prüfen, ob die statischen Daten initialisiert werden und es dann tatsächlich initialisieren.

public class MyClass {

    static private Properties propTable;

    static
    {
        try 
        {
            propTable.load(new FileInputStream("/data/user.prop"));
        } 
        catch (Exception e) 
        {
            propTable.put("user", System.getProperty("user"));
            propTable.put("password", System.getProperty("password"));
        }
    }

Vergleich

public class MyClass 
{
    public MyClass()
    {
        synchronized (MyClass.class) 
        {
            if (propTable == null)
            {
                try 
                {
                    propTable.load(new FileInputStream("/data/user.prop"));
                } 
                catch (Exception e) 
                {
                    propTable.put("user", System.getProperty("user"));
                    propTable.put("password", System.getProperty("password"));
                }
            }
        }
    }

Vergessen Sie nicht, Sie haben jetzt in der Klasse zu synchronisieren, nicht Instanzebene. Dies verursacht eine Kosten für jede Instanz statt einer einmaligen Kosten aufgebaut, wenn die Klasse geladen wird. Plus, es ist hässlich; -)

las ich einen ganzen Artikel für eine Antwort auf die init Reihenfolge der initializers gegen ihre Erbauer suchen. Ich fand es nicht, so schrieb ich einen Code, mein Verständnis zu überprüfen. Ich dachte, ich würde diese kleine Demonstration als Kommentar hinzuzufügen. Um Ihr Verständnis zu testen, ob Sie die Antwort vorhersagen können, bevor sie am Boden zu lesen.

/**
 * Demonstrate order of initialization in Java.
 * @author Daniel S. Wilkerson
 */
public class CtorOrder {
  public static void main(String[] args) {
    B a = new B();
  }
}

class A {
  A() {
    System.out.println("A ctor");
  }
}

class B extends A {

  int x = initX();

  int initX() {
    System.out.println("B initX");
    return 1;
  }

  B() {
    super();
    System.out.println("B ctor");
  }

}

Ausgabe:

java CtorOrder
A ctor
B initX
B ctor

Ein statischer Initialisierer ist die äquivalent einen Konstruktors im statischen Kontext. Sie werden sehen, sicher, dass häufiger als eine Instanz initializer. Manchmal müssen Sie Code ausführen, um die statische Umgebung einzurichten.

Im Allgemeinen ist ein Beispiel initalizer ist am besten für anonyme innere Klassen. Schauen Sie sich auf JMock Kochbuch eine innovative Möglichkeit, um zu sehen, es zu verwenden, um Code besser lesbar zu machen.

Manchmal, wenn Sie eine gewisse Logik haben, die sich über Konstruktoren Kette kompliziert ist (sagen Sie Subklassen und Sie können dies nicht () aufrufen, weil Sie anrufen Super müssen ()), Sie Duplizierung, indem Sie die gemeinsame Sachen vermeiden könnte in der Instanz initalizer. Instance initalizers sind so selten, aber, dass sie eine überraschende Syntax zu viele sind, so dass ich sie vermeiden und würde lieber meine Klasse zu konkretisieren und nicht anonym, wenn ich den Konstruktor Verhalten benötigen.

JMock ist eine Ausnahme, denn das ist, wie das Framework verwendet werden soll.

Es ist ein wichtiger Aspekt, dass Sie in Ihrer Wahl zu berücksichtigen haben:

Initializer Blöcke sind Mitglieder der Klasse / Objekt, während Konstrukteuren nicht sind. Dies ist wichtig, wenn man bedenkt, Erweiterung / Subklassifizieren :

  1. Initializers vererbt von Unterklassen. (Obwohl, kann beschattet werden)
    Das bedeutet, es ist grundsätzlich gewährleistet, dass Unterklassen initialisiert werden von der übergeordneten Klasse bestimmt.
  2. Konstrukteurs sind nicht geerbt , though. (Sie nennen nur super() [das heißt keine Parameter] implizit oder Sie haben einen bestimmten super(...) Anruf manuell machen.)
    Das bedeutet, es möglich ist, dass ein impliziter oder exclicit super(...) Anruf nicht die Unterklasse initialisiert werden kann, wie durch die übergeordnete Klasse bestimmt.

Betrachten Sie dieses Beispiel eines Initialisierungsblocks:

class ParentWithInitializer {
    protected final String aFieldToInitialize;

    {
        aFieldToInitialize = "init";
        System.out.println("initializing in initializer block of: " 
            + this.getClass().getSimpleName());
    }
}

class ChildOfParentWithInitializer extends ParentWithInitializer{
    public static void main(String... args){
        System.out.println(new ChildOfParentWithInitializer().aFieldToInitialize);
    }
}

Ausgabe:
initializing in initializer block of: ChildOfParentWithInitializer init
-.> Egal, was die Unterklasse implementiert Bauer, wird das Feld initialisiert werden

Nun betrachten Sie dieses Beispiel mit Konstrukteuren:

class ParentWithConstructor {
    protected final String aFieldToInitialize;

    // different constructors initialize the value differently:
    ParentWithConstructor(){
        //init a null object
        aFieldToInitialize = null;
        System.out.println("Constructor of " 
            + this.getClass().getSimpleName() + " inits to null");
    }

    ParentWithConstructor(String... params) {
        //init all fields to intended values
        aFieldToInitialize = "intended init Value";
        System.out.println("initializing in parameterized constructor of:" 
            + this.getClass().getSimpleName());
    }
}

class ChildOfParentWithConstructor extends ParentWithConstructor{
    public static void main (String... args){
        System.out.println(new ChildOfParentWithConstructor().aFieldToInitialize);
    }
}

Ausgabe:
Constructor of ChildOfParentWithConstructor inits to null null
-.> Dadurch wird das Feld initialisiert werden standardmäßig null, auch wenn es nicht das Ergebnis sein könnte, man wollte

Ich möchte auch einen Punkt hinzuzufügen, zusammen mit all den oben fabelhaften Antworten. Wenn wir einen Fahrer in JDBC mit Class.forName ( „“) das Laden der Klasse laden geschieht und die statische Initialisierung der Treiber-Klasse wird ausgelöst, und der Code innerhalb registriert sie Treiber zu Treiber-Manager. Dies ist einer der wesentlichen Verwendung von statischem Code-Block.

Wie Sie erwähnt haben, ist es nicht sinnvoll, in vielen Fällen und wie bei jeder weniger verwendete Syntax, möchten Sie wahrscheinlich, es zu vermeiden gerade die nächste Person in Ihrem Code suchen, um zu verhindern verbringen die 30 Sekunden ziehen Sie es aus die Gewölbe.

Auf der anderen Seite ist es die einzige Möglichkeit, ein paar Dinge zu tun (ich glaube, Sie ziemlich viel diejenigen abgedeckt).

Statische Variablen sollten sich etwas ohnehin vermieden werden. - nicht immer, aber wenn Sie eine Menge von ihnen, oder Sie verwenden eine Menge in einer Klasse, können Sie verschiedene Ansätze, Ihre Zukunft selbst finden wird es Ihnen danken

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top