Eine Anleitung zu Konstruktoren in Java

Ein Leitfaden für Konstruktoren in Java

1. Einführung

Konstruktoren sind die Gatekeeper vonobject-oriented design.

In diesem Tutorial werden wir sehen, wie sie als ein einzelner Speicherort fungieren, von dem austhe internal state des zu erstellenden Objekts initialisiert werden.

Lassen Sie uns fortfahren und ein einfaches Objekt erstellen, das ein Bankkonto darstellt.

2. Ein Bankkonto einrichten

Stellen Sie sich vor, wir müssen eine Klasse erstellen, die ein Bankkonto darstellt. Es enthält einen Namen, ein Erstellungsdatum und ein Guthaben.

Überschreiben wir außerdem dietoString-Methode, um die Details auf der Konsole zu drucken:

class BankAccount {
    String name;
    LocalDateTime opened;
    double balance;

    @Override
    public String toString() {
        return String.format("%s, %s, %f",
          this.name, this.opened.toString(), this.balance);
    }
}

Diese Klasse enthält jetzt alle erforderlichen Felder zum Speichern von Informationen zu einem Bankkonto, enthält jedoch noch keinen Konstruktor.

Dies bedeutet, dass beim Erstellen eines neuen Objekts die Feldwerte nicht initialisiert werden:

BankAccount account = new BankAccount();
account.toString();

Das Ausführen der obigen MethodetoString führt zu einer Ausnahme, da die Objektename undopened immer nochnull sind:

java.lang.NullPointerException
    at com.example.constructors.BankAccount.toString(BankAccount.java:12)
    at com.example.constructors.ConstructorUnitTest
      .givenNoExplicitContructor_whenUsed_thenFails(ConstructorUnitTest.java:23)

3. Ein Konstruktor ohne Argumente

Beheben wir das mit einem Konstruktor:

class BankAccount {
    public BankAccount() {
        this.name = "";
        this.opened = LocalDateTime.now();
        this.balance = 0.0d;
    }
}

Beachten Sie ein paar Dinge über den Konstruktor, den wir gerade geschrieben haben. Erstens ist es eine Methode, aber es gibt keinen Rückgabetyp. Dies liegt daran, dass ein Konstruktor implizit den Typ des von ihm erstellten Objekts zurückgibt. Wenn Sie jetzt new BankAccount() aufrufen, wird der obige Konstruktor aufgerufen.

Zweitens braucht es keine Argumente. Diese spezielle Art von Konstruktor wird als no-argument constructor bezeichnet.

Warum brauchten wir es aber nicht zum ersten Mal? Es liegt daran, wenn wirdon’t explicitly write any constructor, the compiler adds a default, no-argument constructor.

Aus diesem Grund konnten wir das Objekt zum ersten Mal erstellen, obwohl wir keinen Konstruktor explizit geschrieben haben. Der Standardkonstruktor ohne Argumente setzt einfach alle Mitglieder auf ihredefault values.

Für Objekte ist diesnull,, was zu der Ausnahme führte, die wir zuvor gesehen haben.

4. Ein parametrisierter Konstruktor

Ein wirklicher Vorteil von Konstruktoren besteht darin, dass sie uns helfen,encapsulation beizubehalten, wenn der Zustand in das Objekt eingefügt wird.

Um also mit diesem Bankkonto etwas wirklich Nützliches zu machen, müssen wir in der Lage sein, einige Anfangswerte in das Objekt einzufügen.

Dazulet’s write a parameterized constructor, that is, a constructor that takes some arguments:

class BankAccount {
    public BankAccount() { ... }
    public BankAccount(String name, LocalDateTime opened, double balance) {
        this.name = name;
        this.opened = opened;
        this.balance = balance;
    }
}

Jetzt können wir mit unsererBankAccount-Klasse etwas Nützliches tun:

    LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00);
    BankAccount account = new BankAccount("Tom", opened, 1000.0f);
    account.toString();

Beachten Sie, dass unsere Klasse jetzt 2 Konstruktoren hat. Ein expliziter Konstruktor ohne Argumente und ein parametrisierter Konstruktor.

Wir können so viele Konstruktoren erstellen, wie wir möchten, aber wir möchten wahrscheinlich nicht zu viele erstellen. Das wäre etwas verwirrend.

Wenn wir zu viele Konstruktoren in unserem Code finden, können einigeCreational Design Patterns hilfreich sein.

5. Ein Kopierkonstruktor

Konstruktoren müssen nicht nur auf die Initialisierung beschränkt sein. Sie können auch zum Erstellen von Verhalten verwendet werden. Imagine that we need to be able to create a new account from an existing one.

Das neue Konto sollte den gleichen Namen wie das alte Konto haben, das heutige Erstellungsdatum und kein Guthaben. We can do that using a copy constructor:

public BankAccount(BankAccount other) {
    this.name = other.name;
    this.opened = LocalDateTime.now();
    this.balance = 0.0f;
}

Jetzt haben wir folgendes Verhalten:

LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00);
BankAccount account = new BankAccount("Tim", opened, 1000.0f);
BankAccount newAccount = new BankAccount(account);

assertThat(account.getName()).isEqualTo(newAccount.getName());
assertThat(account.getOpened()).isNotEqualTo(newAccount.getOpened());
assertThat(newAccount.getBalance()).isEqualTo(0.0f);

6. Ein verketteter Konstruktor

Natürlich können wir möglicherweise einige der Konstruktorparameter odergive some of them default values. ableiten

Beispielsweise könnten wir nur ein neues Bankkonto mit dem Namen erstellen.

Erstellen wir also einen Konstruktor mit einemname-Parameter und geben Sie den anderen Parametern Standardwerte:

public BankAccount(String name, LocalDateTime opened, double balance) {
    this.name = name;
    this.opened = opened;
    this.balance = balance;
}
public BankAccount(String name) {
    this(name, LocalDateTime.now(), 0.0f);
}

Mit dem Schlüsselwortthis, rufen wir den anderen Konstruktor auf.

Wir müssen uns daran erinnern, dassif we want to chain a superclass constructor we have to use super instead of this.

Denken Sie auch daran, dassthis or super expression should always be the first statement.

7. Werttypen

Eine interessante Verwendung von Konstruktoren in Java ist die Erstellung vonValue Objects. A value object is an object that does not change its internal state after initialization.

That is, the object is immutable. Die Unveränderlichkeit in Java beträgt etwasnuancedund beim Erstellen von Objekten ist Vorsicht geboten.

Lassen Sie uns fortfahren und eine unveränderliche Klasse erstellen:

class Transaction {
    final BankAccount bankAccount;
    final LocalDateTime date;
    final double amount;

    public Transaction(BankAccount account, LocalDateTime date, double amount) {
        this.bankAccount = account;
        this.date = date;
        this.amount = amount;
    }
}

Beachten Sie, dass wir jetzt das Schlüsselwortfinalverwenden, wenn wir die Mitglieder der Klasse definieren. Dies bedeutet, dass jedes dieser Elemente nur im Konstruktor der Klasse initialisiert werden kann. Sie können später in keiner anderen Methode neu zugewiesen werden. Wir können diese Werte lesen, aber nicht ändern.

If we create multiple constructors for the Transaction class, each constructor will need to initialize every final variable. Wenn Sie dies nicht tun, wird ein Kompilierungsfehler angezeigt.

8. Fazit

Wir haben eine Tour durch die verschiedenen Arten gemacht, wie Konstruktoren Objekte erstellen. Konstrukte bilden die Grundbausteine ​​des objektorientierten Entwurfs in Java.

Wie immer finden sich Codebeispiele inover on GitHub.