javaseiten.de   |   Version 0.6
 

1.4. Konstruktoren und statische Initialisierer

Ein Konstruktor wird bei der Erzeugung einer neuen Instanz einer Klasse aufgerufen. Für den Begriff Konstruktor wird auch Instanz-Initialisierungs-Methode oder Konstruktormethode verwendet. Die Deklaration eines Konstruktors ähnelt der einer Methode, wobei eine Konstruktordeklaration keine Typangabe für einen Rückgabewert hat. Der Name eines Konstruktors ist immer festgelegt. Er ist gleich dem (einfachen) Namen der Klasse, in der der Konstruktor deklariert ist. Die Beispielklasse ContructorExample deklariert einen Konstruktor mit zwei Parametern.

Listing 1.15. ConstructorExample.java

/* ConstructorExample.java */

public class ConstructorExample {
  
  int a;
  int b;
  
  public ConstructorExample(int a, int b) {
    this.a = a;
    this.b = b;
  }

  public static void main(String[] args) { 
    ConstructorExample ce = new ConstructorExample(3, 4);
    System.out.println(ce.a + ", " + ce.b);
  }
}

Der Aufruf des Konstruktors erfolgt nach dem new-Operator. Dem Konstruktor werden die beiden Argumente 3 und 4 übergeben. Innerhalb des Konstruktors wird das Schlüsselwort this verwendet.

this-Zeiger. this speichert eine Referenz auf das aktuelle Objekt und kann daher verwendet werden, um die Instanzvariablen a und b mit den Werten der Parametervariablen a und b zu initialisieren. Eine Möglichkeit ohne Verwendung des this-Zeigers wäre, die Parameternamen zu ändern.

public ConstructorExample(int x, int y) {
  a = x;
  b = y;
}

Der Java-Compiler erkennt in diesem Fall, dass a und b keine lokalen Variablen des Konstruktors sind und versucht daher Variablenzugriffe auf mögliche Instanzvariablen im Sinne von this.a und this.b. Im Beispiel sind diese Instanzvariablen vorhanden und die gewünschte Initialisierung dieser wird durchgeführt. endmarker.gif

Wie bei "normalen" Methoden können auch Konstruktoren überladen werden. Das Listing 1.16 enthält insgesamt drei Konstruktoren. Der Name des Konstruktors ConstructorExample2 muss darin mit dem Namen der Klasse übereinstimmen. Eine Unterscheidung der Konstruktoren ist möglich, da sie unterschiedliche Parameterlisten besitzen, wobei der letzte der drei angegebenen Konstruktoren eine variable Parameterliste deklariert.

Listing 1.16. ConstructorExample2.java. Programmbeispiel mit überladenem Konstruktor.

/* ConstructorExample2.java */

public class ConstructorExample2 {
  
  int a;
  int b;
  int c;
  String str;
    
  public ConstructorExample2(int a, int b) {
    this.a = a;
    this.b = b;
  }
  
  public ConstructorExample2(String str) {
    this.str = str;
  }
  
  public ConstructorExample2(int... varparas) {
    for (int i : varparas) {
      this.c = this.c + i; 
    }
  }

  public static void main(String[] args) {
    ConstructorExample2 ce21 = new ConstructorExample2(3, 4);
    System.out.println(ce21.a + ", " + ce21.b + ", " +
                       ce21.c + ", " + ce21.str);
    
    ConstructorExample2 ce22 = new ConstructorExample2("Hello World!");
    System.out.println(ce22.a + ", " + ce22.b + ", " +
                       ce22.c + ", " + ce22.str);
    
    ConstructorExample2 ce23 = new ConstructorExample2(5, 6, 7);
    System.out.println(ce23.a + ", " + ce23.b + ", " +
                       ce23.c + ", " + ce23.str);
  }
}

Beim Aufruf der Konstruktoren während der Erzeugung eines neuen Objekts, wird der "passende" Konstruktor automatisch ausgewählt. Die Anzahl der beim Aufruf des Konstruktors zu übergebenden Argumente müssen mit den vorhandenen Konstruktor-Parameterlisten "in Einklang" gebracht werden können. Des Beispiel gibt drei Zeilen auf dem Bildschirm aus:

3, 4, 0, null
0, 0, 0, Hello World!
0, 0, 18, null

Der letzte Konstruktoraufruf erfolgt mit drei Ganzzahlen. In diesem Fall wird der Konstruktor mit variabler Parameterliste ausgewählt. Er berechnet die Summe der drei Zahlen, die anschließend durch die Instanzvariable c gespeichert wird. Beim Konstruktoraufruf mit den Zahlen 3 und 4 als Argumenten wird der "passgenauere" Konstruktor ausgewählt. Im Beispiel ist das derjenige, der zwei Parameter vom Typ int deklariert. Auch der zuletzt angegebene Konstruktor mit variabler Parameterliste könnte diesen Aufruf handhaben. Beim Weglassen des zuerst deklarierten Konstruktors würde dies auch der Fall sein.

 

Default-Konstruktor

Ein Default-Konstruktor wird automatisch generiert, falls kein Konstruktor innerhalb einer Klassendeklaration angegeben wird. Bei der Übersetzung mittels javac wird gegebenenfalls der parameterlose Default-Konstruktor in die erzeugte .class-Datei aufgenommen. Das Programm aus Listing 1.2 enthält z.B. keinen Konstruktor. Die zughörige Klassendatei kann mit Hilfe des Java-Tools javap untersucht werden. Dabei wird deutlich, dass der Klassenkörper neben der main-Methode noch einen durch den Compiler automatisch erzeugten Default-Konstruktor enthält.

> javac HelloWorld.java

> javap HelloWorld
Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object {
  public HelloWorld();
  public static void main(java.lang.String[]);
}

Ein parameterloser Default-Konstruktor hat die Aufgabe, den Konstruktor der Superklasse ohne Argumente aufzurufen. Die Ausgabe von javap zeigt, dass die direkte Superklasse von HelloWorld die Klasse Object ist. Object ist im Paket java.lang enthalten und jede Java-Klasse hat definitionsgemäß Object als eine Superklasse. Durch das Schlüsselwort extends wird angezeigt, dass die Beispielklasse HelloWorld eine direkte Subklasse von Object ist. Die Vererbungsmechanismen der Programmiersprache Java sollen an späterer Stelle in Abschnitt 1.7 erläutert werden. Im Quelltext HelloWorld.java wird die optionale extends-Klausel nicht verwendet. In diesem Fall gilt: Wird keine extends-Klausel bei einer Klassendeklaration verwendet, dann ist die Superklasse der neuen Klasse die Klasse Object. Das Tool javap ist ein Disassembler für Java-Klassendateien und kann mit der Option -c verwendet werden, um den automatisch erzeugten Default-Konstruktor auf Bytecodeebene näher zu untersuchen. Die Mnemonik invokespecial ist Teil des Default-Konstruktors und kennzeichnet den Aufruf des entprechenden Konstruktors der Superklasse Object (siehe dazu auch Abschnitt 4).

> javap -c HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object {

public HelloWorld();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   ...
}

Das Beispiel aus Listing 1.15 enthält einen Konstruktor mit zwei Parametern. In diesem Fall wird bei der Übersetzung des Java-Quelltextes durch javac kein Default-Konstruktor in die Klassendatei geschrieben. Der angegebene Konstruktor im Quelltext wird in eine Zahlenrepräsentation (Bytecode) innerhalb der Klassendatei übersetzt. Der erzeugte "Bytecode-Konstruktor" enthält am Beginn ebenfalls einen Aufruf des entsprechenden Konstruktors der Superklasse Object, bevor die beiden Felder initialisiert werden. Der Java-Compiler sorgt also im Beispiel eigenständig für eine korrekte Abfolge von Konstruktoraufrufen im Hinblick auf Vererbungsmechanismen.

 

Statische Initialisierer

Die Deklaration von Feldern kann die Initialisierung dieser beinhalten. Die Zuordnung von Werten für statische Variablen kann auch in einen statischen Initialisierer "ausgelagert" werden. Er beginnt mit dem Schlüsselwort static. Innerhalb der geschweiften Klammern können dann die gewünschten Initialisierungen vorgenommen werden.

static {
  ...
}

Als Beispiel können die anschließenden Deklarationen von drei Klassenvariablen dienen, deren Wertezuordnungen in einen statischen Initialisierer übertragen werden sollen.

static int i = 3;
static float pi = 3.1415f;
static String[] str = {"eins", "zwei"};

Eine äquivalente Initialisierung der drei statischen Variablen mit Hilfe eines statischen Initialisierers könnte dann wie folgt aussehen:

Listing 1.17. StaticInitializerExample.java. Auslagern von Initialisierungen von statischen Variablen (Klassenvariablen) in einen statischen Initialisierer.

/* StaticInitializerExample.java */

public class StaticInitializerExample {
  
  static int i;
  static float pi;
  static String[] str;
  
  static {
    i = 3;
    pi = 3.1415f;
    str = new String[]{"eins", "zwei"};
  }
  
  public static void main(String args[]) {
    System.out.println(StaticInitializerExample.i + ", " + 
                       StaticInitializerExample.pi + ", " +
                       StaticInitializerExample.str[0] + ", " +
                       StaticInitializerExample.str[1]);
  }
}

Das Beispielprogramm gibt "3, 3.1415, eins, zwei" auf dem Bildschirm aus. Statische Initialisierer werden im Rahmen des Initialisierungsprozesses einer Klasse aufgerufen. Der statische Initialisierer im Java-Quelltext wird in einen statischen Initialisierer innerhalb der zugehörigen Klassendatei übersetzt. Die erzeugte Klassendatei StaticInitializerExample.class enthält im Beispiel ebenfalls einen statischen Initialisierer, nur entsprechend bytecodiert. Auch wenn die Initialisierung der statischen Variablen direkt erfolgt, also ohne die explizite Angabe eines statischen Initialisierers im Quelltext, wird durch den Java-Compiler innerhalb der Klassendatei ein separater statischer Initialisierer erzeugt. Die beiden folgenden Codefragmente führen daher zu identischen Abbildungen auf eine Klassendatei (dies kann z.B. überprüft werden, indem javap -c auf die entprechenden Klassendateien angewendet wird und die erzeugten Ausgaben miteinander verglichen werden).

Codefragment 1:
    static int i = 3;
    static float pi = 3.1415f;
    static String[] str = {"eins", "zwei"};

Codefragment 2:
    static int i;
    static float pi;
    static String[] str;
    static {
      i = 3;
      pi = 3.1415f;
      str = new String[]{"eins", "zwei"};
    }

 

 

 

Diese Seite nutzt Google-Dienste - siehe dazu Datenschutz.

Copyright © 2006, 2007 Harald Roeder