Advanced   Java   Services Vererbung 2 Back Next Up Home

this als Konstruktorname

Eine reichhaltige Konstruktorausstattung ist für den Benutzer bequem, aber für den Entwickler der Klasse ein wenig lästig. Der Rumpf der Konstruktoren ist meist sehr ähnlich ist, so daß in den Konstruktoren "fast" das gleiche steht. Die Klasse Person braucht etwa neben dem Defaultkonstruktor eigentlich nur einen Konstruktor der Form Person(String v, String n) . Der Anwender muß dann halt statt p = new Person("Kandinsky") den Aufruf p = new Person("", "Kandinsky") verwenden.

Es gibt jedoch die Möglichkeit, sich innerhalb eines Konstruktors auf einen anderen Konstruktor der gleichen Klasse zu beziehen. Dazu dient das Schlüsselwort this , das völlig analog zu super verwendet wird. Wir demonstrieren dies zunächst mit einer weiteren Variante der Klasse Person.

public class Person
{
   // Membervariablen
   private String vor ;
   private String nach;

   // Konstruktoren
   public Person()
   {
      this("","")  ;
   }
   public Person(String n)
   {
      this("", n)  ;
   }
   public Person(String v, String n)
   {
      vor = v ;
      nach = n ;
   }

   // ....

}

Hier wird mit  this( "" , "" )  aus dem Defaultkonstruktor der Konstruktor  Person( String v , String n )  aufgerufen. Analog wird aus dem Konstruktor Person(String n) mit Hilfe von  this( "" , n )  ebenfalls der dritte Konstruktor aufgerufen.

Zu beachten ist, daß this analog wie super ebenfalls als erstes Statement in einem Konstruktor stehen muß. Daraus ergibt sich zwingend, daß this und super nicht zusammen eingesetzt werden können, sondern nur alternativ.


Anonymer Konstruktor (anonymous initializer)

Mit this() bezieht man sich auf einen Konstruktor der gleichen Klasse. Man macht dies hauptsächlich deswegen um Initialisierungsarbeiten, die für jedes Objekt notwendig sind, nur einmal zu codieren. Man erledigt diese Arbeit nur einmal in einem Konstruktor und verweist dann mit this() auf diesen Konstruktor. Seit der Einführung innerer und anonymer Klassen gibt es für dieses Vorgehen eine elegante Variante, den anonymen Konstruktor. Ein anonymer Konstruktor ist ein namenloser Block, der auf der obersten Ebene der Klasse angesiedelt ist. Tieferliegende Blöcke (also Blöcke in Methoden oder Konstruktoren) haben diese Eigenschaft (natürlich) nicht. Betrachten wir das folgende Beispiel:

class Anonymous
{
   // Membervariablen
   // ...

   // anonymer Konstruktor
   {
      System.out.println("anonymous initializer");
   }

   // Konstruktoren
   public Anonymous()
   {
      System.out.println("Anonymous()");
   }
   public Anonymous(int a)
   {
      System.out.println("Anonymous(int a)");
   }

   // Methoden
   // ...

}  // end class Anonymous


public class AnonymousDemo
{
   public static void main(String[] args)
   {
      Anonymous an1 = new Anonymous();
      System.out.println("---------------------");
      Anonymous an2 = new Anonymous(5);
   }
}

Das kleine Demoprogramm macht folgende Ausgabe:

anonymous initializer
Anonymous()
---------------------
anonymous initializer
Anonymous(int a)

Man sieht, daß der anonyme Konstruktor objektbezogen wirkt und noch vor dem eigentlichen Konstruktor abgearbeitet wird und daß dies für jeden Konstruktor gilt. Damit kann man Initialisierungsarbeiten, die für jedes Objekt anfallen, in diesen namenlosen Initialisierer packen und spart sich damit die Überlegung, auf welchen Konstruktor man sich mit this() beziehen will.


Was macht der Compiler, wenn er einen anonymen Block erkennt

Versucht man am Anfang eines anonymen Konstruktors einen this() oder super()-Aufruf, so erhält man die Fehlermeldung: call to this must be first statement in constructor. Daraus kann man den folgenden Schluß ziehen: Existiert ein namenloser Block, so ergänzt der Compiler diesen Block unmittelbar nach einem super() oder this()- Aufruf, d.h. die folgende Codierung

// anonymer Konstruktor
{
   System.out.println("anonymous initializer");
}

// Konstruktoren
public Anonymous()
{
   System.out.println("Anonymous()");
}
public Anonymous(int a)
{
   System.out.println("Anonymous(int a)");
}

wird vom Compiler so übersetzt:

// Konstruktoren
public Anonymous()
{
   // impliziter oder expliziter Aufruf von super() oder this()
   // anonymer Block wird eingefügt
   // weitere statements
   System.out.println("Anonymous()");
}
public Anonymous(int a)
{
   // impliziter oder expliziter Aufruf von super() oder this()
   // anonymer Block wird eingefügt
   // weitere statements
   System.out.println("Anonymous(int a)");
}

Dadurch wird sichergestellt, daß für jeden Konstruktor der anonyme Initialisierer nach dem super()-Aufruf und vor allen anderen Statements kommt.


Statische Daten

In unseren bisherigen Beispielen war der Datenteil eine Klasse immer objektbezogen. Jedes Objekt, das mit Hilfe eines Konstruktors angelegt wird, enthält dann diesen Datenteil als seine Eigenschaften. Manchmal sind jedoch auch Daten nützlich, die nicht objektbezogen sind, sondern klassenbezogen, also Daten, die nur ein einziges Mal pro Klasse angelegt werden. Solche Daten nennen wir in Zukunft statische Daten. Jedes Objekt der Klasse darf auf sie zugreifen, aber sie gehören zu keinem Objekt, sie gehören eben zur Klasse. Die Deklaration von statischen Daten sieht folgendermaßen aus:

public class StaticDemo
{
   // statischer Datenteil
   private static int counter ;

   // nichtstatischer Datenteil
   private String vor, nach ;

   // Konstruktoren
   // ...

   // Methoden
   // ...

}

Und so kann man sich das im Speicher vorstellen:

static2.jpg

Es gibt verschiedene Begriffe, um diesen Unterschied klarzumachen. Statt Datenteil einer Klasse spricht man auch von Membervariablen und unterscheidet dann zwischen Instanzvariablen und Klassenvariablen. Die folgende Graphik macht also zweimal dieselbe Untercheidung mit anderen Begriffen.

variable2.jpg


Statischer Konstruktor (static initializer)

Nichtstatische Daten werden pro Objekt durch einen Konstruktor initialisiert. Es liegt nahe, etwas ähnliches für statische Daten zu erwarten. Tatsächlich gibt es einen statischen Konstruktor bzw. einen statischen Initialisierer. Wie ein anonymer Block liegt er auf der obersten Ebene der Klasse, wird jedoch durch das Schlüsselwort static eingeleitet:

public class StaticDemo
{
   // statischer Datenteil

   // nichtstatischer Datenteil

   //statischer Konstruktor
   static
   {
      // statische Initialisierungen
   }

   // Konstruktoren

   // Methoden

}

Wann wird der statische Konstruktor abgearbeitet

Dazu betrachten wir zunächst das folgende Beispiel:

public class StaticDemo
{
   public static void main(String[] args)
   {
      Static s1 = new Static();
      System.out.println("---------------------");
      Static s2 = new Static(5);
   }
}

class Static
{
   // Membervariablen
   // ...
   // ...

   // statischer Konstruktor
   static
   {
      System.out.println("static initializer");
   }
   // anonymer Konstruktor
   {
      System.out.println("anonymous initializer");
   }

   // Konstruktoren
   public Static()
   {
      System.out.println("Static()");
   }
   public Static(int a)
   {
      System.out.println("Static(int a)");
   }

   // Methoden
   // ...
   // ...
}

Es macht die folgende Ausgabe

static initializer
anonymous initializer
Static()
---------------------
anonymous initializer
Static(int a)

Der statische Konstruktor wird also einmal vor dem Anlegen des ersten Objekts aufgerufen. Danach nie mehr. Es gibt aber noch eine zweite Situation, in der die statischen Daten angelegt werden, ohne daß überhaupt ein Objekt existiert.


Statische Methoden und statische Daten

Jetzt, wo wir statische Daten kennengelernt haben, erscheinen die statischen Methoden in einem klareren Licht. Statische Methoden arbeiten mit statischen Daten zusammen. Statische Methoden haben Zugriff auf statische Daten, aber keinen Zugriff auf nichtstatische Daten, da diese objektbezogen sind. Daraus ergibt sich aber auch, daß statische Daten beim ersten Aufruf einer statischen Methode vorhanden sein müssen, also muß der statische Initialisierer vor dem Aufruf abgearbeitet sein. Das folgende Beispiel demonstriert dies:

public class StaticDemo2
{
   public static void main(String[] args)
   {
      Static2 s1;
      // Hier wird nur eine Referenz auf ein Objekt angelegt

      Static2.staticMethod();
   }
}

class Static2
{
   // Membervariablen
   // ...
   // ...

   // statischer Konstruktor
   static
   {
      System.out.println("static initializer");
   }
   // anonymer Konstruktor
   {
      System.out.println("anonymous initializer");
   }

   // static method
   public static void staticMethod()
   {
      System.out.println("staticMethod()");
   }

} // end class Static2

Das Programm macht die folgende Ausgabe

static initializer
staticMethod()

Beachten Sie, daß der anonyme Initialisierer nicht abgearbeitet wird ! Ein Standardbeispiel für einen statischen Datenteil ist ein Zähler, der die Anzahl der Objekte einer Klasse zählt. Wir werden das als Übung erledigen.


Zusammenfassung

Übungen

Ein Counter für die Klasse Person

Reihenfolge der Initialisierung bei Vererbung

Valid XHTML 1.0 Strict top Back Next Up Home