Wie unterscheidet sich ein Instanzinitialisierer von einem Konstruktor?

82

Mit anderen Worten, warum benötigen Sie einen Instanzinitialisierer? Welchen Unterschied oder Vorteil haben Sie beim Schreiben eines Instanzinitialisierers gegenüber einem Konstruktor?

Ajay
quelle
2
Instanzinitialisierer sind ziemlich selten (es sei denn, Sie stoßen auf Code von jemandem, der sich für die Doppelklammer-Sprache interessiert).
Tom Hawtin - Tackline

Antworten:

109

Dies scheint es gut zu erklären:

Instanzinitialisierer sind eine nützliche Alternative zu Instanzvariableninitialisierern, wenn:

  • Der Initialisierungscode muss Ausnahmen abfangen, oder

  • Führen Sie ausgefallene Berechnungen durch, die mit einem Instanzvariablen-Initialisierer nicht ausgedrückt werden können. Sie können solchen Code natürlich immer in Konstruktoren schreiben.

In einer Klasse mit mehreren Konstruktoren müssten Sie jedoch den Code in jedem Konstruktor wiederholen. Mit einem Instanzinitialisierer können Sie den Code nur einmal schreiben. Er wird ausgeführt, unabhängig davon, mit welchem ​​Konstruktor das Objekt erstellt wird. Instanzinitialisierer sind auch in anonymen inneren Klassen nützlich, die überhaupt keine Konstruktoren deklarieren können.

Von: JavaWorld- Objektinitialisierung in Java .

javamonkey79
quelle
17
Auf der anderen Seite können Sie Code einmal in einen Konstruktor schreiben und ihn einfach von allen anderen Konstruktoren aufrufen. Aber anonyme innere Klassen machen einen guten Punkt.
Tadeusz Kopec
11
Sie können es von anderen Konstruktoren aus aufrufen - aber dann wiederholen Sie den Aufruf erneut. Wenn Sie einen neuen Konstruktor hinzufügen, müssen Sie daran denken, den Aufruf hinzuzufügen. Nicht so bei einem Instanzinitialisierer.
Talonx
5
@talonx, ich stimme Ihrem Argument über das Vergessen zu, aber die Verwendung von Standardverhalten ist ebenso gefährlich. Wenn ein Unterstützer einen Konstruktor in Legacy-Code durchliest, würde man nicht immer daran denken, nach möglichen Instanzinitialisierern zu suchen. Während ein explizit verwendetes init () auffällt.
Assambar
1
@ javamonkey79: Wollen Sie damit sagen , dass , wenn ich wählen Konstruktor über Instanzeninitialisierung für meine Klasse, der einzige Ort , wo Instanzeninitialisierung nützlich ist , ist , wenn sie mit anonymen Klassen zu arbeiten?
RealPK
4
@Assambar Sie können in Ihrer init () -Methode keine endgültigen Felder zuweisen, aber Sie können in einem Initialisierungsblock.
Timmos
22

In Bezug auf den Objektlebenszyklus gibt es keinen Unterschied. Beide werden zur Konstruktionszeit aufgerufen, und logischerweise kann der Initialisierungsblock als Teil der Konstruktion betrachtet werden.

Semantisch gesehen ist ein Initialisierer aus mehreren Gründen ein gutes Werkzeug:

Der Initialisierer kann die Lesbarkeit des Codes verbessern, indem die Initialisierungslogik neben der zu initialisierenden Variablen bleibt:

   public class Universe {
       public int theAnswer;
       {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;
       }

       // a bunch of other vars
   }

vs.

   public class Universe {
       public int theAnswer;

       // a bunch of other vars

       public Universe() {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;

         // other constructor logic
       }
   }

Initialisierer werden unabhängig davon aufgerufen, welcher Konstruktor verwendet wird.

Initialisierer können in anonymen inneren Klassen verwendet werden, Konstruktoren nicht.

ykaganovich
quelle
3
Was Sie haben, ist technisch gesehen ein "Instanzvariableninitialisierer", kein "Instanzinitialisierer" (ein Block, der direkt in einer Klasse verschachtelt ist). Siehe JLS 3. Abschnitt 8.6.
Tom Hawtin - Tackline
Der Instanzinitialisierungsblock hat keinen Namen wie hier gezeigt . Ist es nicht? Sie haben Ihren Instanzinitialisierungscode mit dem Namen markiert theAnswer. Ist das korrekt? oder mir fehlt etwas.
RBT
1
@RBT theAnswerist eine deklarierte Instanzvariable. Es wird in einem anonymen Initialisierungsblock initialisiert. Beachten Sie das Semikolon nach der Variablendeklaration.
Ykaganovich
10

Wenn Sie viele Konstruktoren haben und möchten, dass für jeden Konstruktor ein allgemeiner Code ausgeführt wird, können Sie den Instanzinitialisierer verwenden. Dies wird für alle Konstruktoren aufgerufen.

Rahul Garg
quelle
4

Ich würde das Idiom des Instanzinitialisierers im Allgemeinen vermeiden - der einzige wirkliche Vorteil, den es gegenüber variablen Initialisierern bietet, ist die Ausnahmebehandlung.

Und da eine init-Methode (vom Konstruktor aus aufrufbar) auch die Ausnahmebehandlung durchführen und den Konstruktor-Setup-Code zentralisieren kann, aber den Vorteil hat, dass sie mit Konstruktorparameterwerten arbeiten kann, würde ich sagen, dass der Instanzinitialisierer redundant ist und daher redundant sein muss vermieden.

CPerkins
quelle
1
Sie müssten die init-Methode in ALLEN Konstruktoren manuell aufrufen.
Stephan
2
@Alex Guter Punkt, aber da die meisten Klassen mit mehreren Konstruktoren eine gemeinsame Logik haben, neigen sie dazu, sich trotzdem gegenseitig aufzurufen. Zumindest in den meisten meiner Klassen.
CPerkins
3

Der wahre Vorteil von Instanzinitialisierern gegenüber Konstruktoren zeigt sich, wenn wir eine anonyme innere Klasse verwenden .

Anonyme innere Klassen können keinen Konstruktor haben (da sie anonym sind) , daher eignen sie sich ziemlich gut für beispielsweise Initialisierer .

Padippist
quelle
1

Wenn wir zum Zeitpunkt der Objekterstellung die Initialisierung von Instanzvariablen durchführen möchten, sollten wir uns für den Konstruktor entscheiden, abgesehen von der Initialisierungsaktivität, wenn wir zum Zeitpunkt der Objekterstellung eine Aktivität ausführen möchten, sollten wir uns für den Instanzblock entscheiden.

Wir können den Konstruktor nicht durch den Instanzblock ersetzen, da der Konstruktor Argumente annehmen kann, der Instanzblock jedoch keine Argumente annehmen kann.

Wir können den Instanzblock nicht durch den Konstruktor ersetzen, da eine Klasse mehr als einen Konstruktor enthalten kann. Wenn wir den Instanzblock durch einen Konstruktor ersetzen möchten, müssen wir in jedem Konstruktor einen Instanzblockcode schreiben, da wir zur Laufzeit nicht erwarten können, welcher Konstruktor aufgerufen wird. Dies erhöht unnötigerweise den doppelten Code.

Beispiel:

class MyClass{

    static int object_count = 0;

    MyClass(){
        object_count++;
    }

    MyClass(int i){

        object_count++;
    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Ausgabe : 2

class MyClass{

    static int object_count = 0;

    {
        object_count++;
    }

    MyClass(){

    }

    MyClass(int i){     

    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Ausgabe : 2

NPE
quelle
0

Der Initializer ist eine Möglichkeit, Code zwischen Konstruktoren gemeinsam zu nutzen, und macht den Code lesbarer, wenn der Initialisierer mit der Variablendeklaration verwendet wird.

Der Java-Compiler kopiert Initialisierungsblöcke in jeden Konstruktor. Daher kann dieser Ansatz verwendet werden, um einen Codeblock zwischen mehreren Konstruktoren zu teilen. Oracle-Dokumentation

dnyanesh
quelle