Javaschubla.de - Java als erste Programmiersprache

OO04 Konstruktoren

Bei der Erzeugung eines Objekts wird der Konstruktor der zugehörigen Klasse aufgerufen.

So sieht unsere Angestellten-Klasse jetzt aus:

class Angestellter
{
  String vorname;
  String nachname;
  int alter;
  int gehalt;

  void erhöheGehalt(int erhöhung)
  {
    if (erhöhung <= 0)
    {
      System.err.println("Das ist aber keine Erhöhung!");
    }
    else
    {
      gehalt += erhöhung;
    }
  }

  void geburtstagFeiern()
  {
    alter++;
    System.out.println("Happy birthday " + vorname + " " + nachname + "!");
  }
}

Und so das Programm, das die Klasse Angestellter verwendet:

class AngestelltenVerwaltung
{
  public static void main(String[] args)
  {
     // Angestellte Müller anlegen
    Angestellter mül = new Angestellter();
    mül.vorname = "Petra";
    mül.nachname = "Müller";
    mül.alter = 45;
    mül.gehalt = 1800;
     // Angestellten Wawrzyniak anlegen
    Angestellter waw = new Angestellter();
    waw.vorname = "Leszek";
    waw.nachname = "Wawrzyniak";
    waw.alter = 23;
    waw.gehalt = 1300;
    // Gehalt von Herrn Wawrzyniak erhöhen
    System.out.println("Das alte Gehalt von Herrn Wawrzyniak: " + waw.gehalt);
    waw.erhöheGehalt(50); /
    System.out.println("Das neue Gehalt von Herrn Wawrzyniak: " + waw.gehalt);
    mül.geburtstagFeiern();
    System.out.println("Das Alter von Frau Müller: " + mül.alter);
  }
}

Was passiert eigentlich in der Zeile Angestellter mül = new Angestellter(); ? Warum stehen da Klammern hinter Angestellter? Da müsste doch eigentlich eine Methode aufgerufen werden, der nur kein Parameter übergeben wird. Und genau so ist es auch. Es wird eine ganz spezielle Methode aufgerufen, die zum Erstellen des Objekts da ist, nämlich ein Konstruktor. (Man würde Konstruktoren deshalb meistens auch nicht als Untermenge der Methoden betrachten.)

Aber wo ist er denn, dieser Konstruktor? Man sieht ihn ja gar nicht in der Klasse? Genau, dieser Default-Konstruktor wird beim Kompilieren automatisch hinzugefügt, wenn keiner da ist, damit man überhaupt ein Objekt von diesem Typ (also eine Instanz dieser Klasse) erzeugen kann. Fügen wir ihn mal explizit hinzu:

class Angestellter
{
  String vorname;
  String nachname;
  int alter;
  int gehalt;

  Angestellter()
  {
    System.out.println("Jetzt wird der Konstruktor ausgeführt.");
  }

  void erhöheGehalt(int erhöhung)
  {
    if (erhöhung <= 0)
    {
      System.err.println("Das ist aber keine Erhöhung!");
    }
    else
    {
      gehalt += erhöhung;
    }
  }

  void geburtstagFeiern()
  {
    alter++;
    System.out.println("Happy birthday " + vorname + " " + nachname + "!");
  }
}

Wenn man nun das Programm AngestelltenVerwaltung ausführt, wird in den beiden Zeilen Angestellter mül = new Angestellter(); und Angestellter waw = new Angestellter(); jeweils "Jetzt wird der Konstruktor ausgeführt." ausgegeben.

Wie man sieht

Wie wir schon mehrmals gesehen haben, ruft man den Konstruktor einer Klasse mit new auf. Konstruktoren sind also ganz besondere Methoden, wenn man sie überhaupt als Methoden betrachten will.

Ein Konstruktor, der nur "Jetzt wird der Konstruktor ausgeführt." ausgibt, ist natürlich nicht sehr sinnvoll. Was sollte ein richtiger Konstruktor tun? Konstruktoren sind dazu da, die Attribute zu initialisieren. Die Werte, die die Attribute erhalten sollen, werden als Parameter übergeben. Und so sehen dann die beiden Klassen aus:

class Angestellter
{
  String vorname;
  String nachname;
  int alter;
  int gehalt;

  Angestellter(String v, String n, int a, int g)
  {
    vorname = v;
    nachname = n;
    alter = a;
    gehalt = g;
  }

  void erhöheGehalt(int erhöhung)
  {
    if (erhöhung <= 0)
    {
      System.err.println("Das ist aber keine Erhöhung!");
    }
    else
    {
      gehalt += erhöhung;
    }
  }

  void geburtstagFeiern()
  {
    alter++;
    System.out.println("Happy birthday " + vorname + " " + nachname + "!");
  }
}
class AngestelltenVerwaltung
{
  public static void main(String[] args)
  {
     // Angestellte Müller anlegen
    Angestellter mül = new Angestellter("Petra", "Müller", 45, 1800);
     // Angestellten Wawrzyniak anlegen
    Angestellter waw = new Angestellter("Leszek", "Wawrzyniak", 23, 1300);
    // Gehalt von Herrn Wawrzyniak erhöhen
    System.out.println("Das alte Gehalt von Herrn Wawrzyniak: " + waw.gehalt);
    waw.erhöheGehalt(50); /
    System.out.println("Das neue Gehalt von Herrn Wawrzyniak: " + waw.gehalt);
    mül.geburtstagFeiern();
    System.out.println("Das Alter von Frau Müller: " + mül.alter);
  }
}

Na, das sieht doch viel übersichtlicher aus, nicht wahr? Nicht nur das, es ist jetzt auch unmöglich, zu vergessen, eines der Attribute zu initialisieren.

Die alte Art funktioniert jetzt nicht mehr:

Angestellter a = new Angestellter();
a.vorname = "Peter";

würde einen Fehler verursachen. Denn da es jetzt einen Konstruktor gibt, wird nicht mehr automatisch der Default-Konstruktor erstellt beim Kompilieren. Unser Konstruktor erwartet zwei Strings und zwei ints, man kann ihn also nicht ohne Parameter aufrufen.

Wenn wir wollten, dass beide Varianten funktionieren, könnten wir einfach zwei Konstruktoren haben:

class Angestellter
{
  String vorname;
  String nachname;
  int alter;
  int gehalt;

  Angestellter()
  {
  }

  Angestellter(String v, String n, int a, int g)
  {
    vorname = v;
    nachname = n;
    alter = a;
    gehalt = g;
  }

  void erhöheGehalt(int erhöhung)
  {
    if (erhöhung <= 0)
    {
      System.err.println("Das ist aber keine Erhöhung!");
    }
    else
    {
      gehalt += erhöhung;
    }
  }

  void geburtstagFeiern()
  {
    alter++;
    System.out.println("Happy birthday " + vorname + " " + nachname + "!");
  }
}

Aber das wollen wir natürlich nicht, weil dann jemand die Objekte verwenden könnte, ohne Namen, Alter und Gehalt zu initialisieren.

Was passiert eigentlich, wenn man das macht? Bei lokalen Variablen gibt es ja eine Fehlermeldung, wenn man eine Variable verwenden will, der man noch keinen Wert zu gewiesen hat:

    int i;
    int a = 2*i; // Geht nicht - Fehlermeldung!
    System.out.println(i); // Geht nicht - Fehlermeldung!

Bei nicht-lokalen Variablen gibt es keine Fehlermeldung. Diese werden automatisch initialisiert. Zahlen zu 0 bzw. 0.0, booleans zu false, chars zu einem Leerzeichen (ASCII/Unicode 0), Objekte (auch Strings und Arrays) zu null (NullPointer - ein Zeiger, der nirgendwohin zeigt). Mit null ist nicht 0 gemeint. null ist ein Literal wie es true und false auch sind. Alle drei müssen genau so, also klein, geschrieben werden.

    Angestellter a = new Angestellter();
    System.out.println(a.name); // gibt null aus
    System.out.println(a.alter); // gibt 0 aus

Der Inhalt von Arrays (auch lokalen Arrays) wird übrigens ebenfalls automatisch initialisiert:

class ArrayInit
{
  public static void main(String[] args)
  {
    int[] array = new int[3];
    System.out.println(array[2]); // gibt 0 aus
  }
}

Mit den Kenntnissen über Konstruktoren ist jetzt die Grundlage für Kapselung und die Verwendung von Modifiern wie private gelegt. Aber dazu etwas später.


Die nächste Lektion werden der Vollständigkeit halber die weniger wichtigen übrigen Operatoren und Konstrollstrukturen erläutert.