Konstruktoren
Konstruktoren Eine Konstruktordeklaration ähnelt einer Methodendeklaration, jedoch wird die Angabe des Metatyps für das Ergebnis (wie beispielsweise »void« oder »int«) weggelassen und an Stelle des Methodennamens der Klassenname eingesetzt. Ein Konstruktor für die Klasse »K« wird damit mit »public K(){ … }« deklariert. Solch ein Konstruktor wird automatisch nach dem Erschaffen eines neuen Objekts aktiviert (ausgeführt), also beispielsweise gegen Ende der Auswertung des Ausdrucks »new K()«. Der Konstruktor kann dann Arbeiten an diesem Objekt erledigen, die jedesmal bei Anlegen eines Objektes dieser Klasse erledigt werden sollen, beispielsweise die Initialisierung (Belegung) nicht-statischer Felder mit bestimmten Werten. Was gibt das folgende Programm demnach aus?
class O
{ final int a;public O(){ java.lang.System.out.println( "O()" ); a = 38; } /* Konstruktor */
public void p(){ java.lang.System.out.println( a ); }}
public final class Main
{ public static void main( final java.lang.String[] args )
{ final O o = new O();
o.p(); }}
Nach der Exemplarerzeugung kann ein Konstruktor das Exemplar initialisieren. Der Konstruktor wird automatisch direkt nach der Exemplarerzeugung aktiviert. Für ein Exemplar der Klasse "Switch" kann er beispielsweise einen Anfangszustand festlegen.
Der Konstruktor wird ähnlich wie eine Methode definiert, deren Name der Name der Klasse ist und die keine Rückgabespezifikation hat. Ein Konstruktor ist jedoch keine Methode und er kann auch nicht wie eine Methode verwendet werden.
Switch.java
class Switch
{ private boolean on;
Switch(){ this.on = true; } /* Konstruktor */
void switchOn (){ on = true; }
void switchOff(){ on = false; }
boolean isOn(){ return on; }}
Invarianten
Parameterloser Konstruktor
Im Konstruktor steht das Schlüsselwort "this" für das zu initialisierende Exemplar.
Main.java
class Object
{ final int a;
final int b;public Object()
{ java.lang.System.out.println( "Object()" ); this.a = 5; this.b = 6; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object = new Object();
java.lang.System.out.println( object.a );
java.lang.System.out.println( object.b ); }}transcript
Object()
5
6
Warum können die Felder final sein?
Konstruktor mit Parametern
Main.java
class Object
{ final int a;
final int b;public Object( final int a, final int b )
{ java.lang.System.out.println( "Object(int,int)" ); this.a = a; this.b = b; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object = new Object( 7, 8 );
java.lang.System.out.println( object.a );
java.lang.System.out.println( object.b ); }}transcript
Object(int,int)
7
8
Konstruktorbezeichnungen
Bestimmte Konstruktoren werden im Englischen manchmal als
- “null constructor”,
- “standard constructor”,
- “general constructor”, oder
- “full constructor”
bezeichnet. Die Bedeutung solcher Begriffe ist aber nicht allgemein definiert und sollte daher von ihren Verwendern jeweils angegeben werden.
Beispiel bei Oracle: “Color c = new Color(0,0,1,1.0); // standard constructor, use 0->1.0 values, explicit alpha of 1.0 ”
null constructor bezeichnet manchmal einen Konstruktor, der alle Felder auf null oder ähnliche Werte setzt, um sie effektiv uninitialisiert zu belassen
standard constructor bezeichnet manchmal einen default constructor, machmal einen full constructor, manchmal etwas anderes
full constructor bezeichnet manchmal einen Konstruktor, der für jedes relevante Feld einen Parameter hat, oder dessen Parameter eine Obermenge der Parameter aller anderen Konstruktoren sind
Überladene Konstruktoren
Main.java
class Object
{ final int a;
final int b;public Object()
{ java.lang.System.out.println( "Object()" ); this.a = 5; this.b = 6; }public Object( final int a, final int b )
{ java.lang.System.out.println( "Object(int,int)" ); this.a = a; this.b = b; }}public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object0 = new Object();
java.lang.System.out.println( object0.a );
java.lang.System.out.println( object0.b );final Object object1 = new Object( 7, 8 );
java.lang.System.out.println( object1.a );
java.lang.System.out.println( object1.b ); }}transcript
Object()
5
6
Object(int,int)
7
8
Überladung
Konstruktoren werden gerne überladen, um einem Klienten die Möglichkeit zur Erzeugung von Exemplaren nach verschiedenen Angaben zu bieten. So könnte es hilfreich sein, einen Schalter auch mit einem bestimmten Anfangswert erzeugen zu können. Dabei kann das Schlüsselwort "this" verwendet werden, um sich auf das zu initialisierende Exemplar zu beziehen.
Switch1.java
class Switch1
{ private boolean on;
Switch1(){ on = true; }
Switch1( final boolean on ){ this.on = on; }
void switchOn (){ on = true; }
void switchOff(){ on = false; }
boolean isOn(){ return on; }}SwitchClient.java
class SwitchClient
{ public static void main( final String[] args )
{ final Switch1 s = new Switch1();
final Switch1 t = new Switch1( false );
System.out.println( s.isOn() );
System.out.println( t.isOn() ); }}System.out
true
false
Fehlkonstruktor
Falls in einer Klasse kein Konstruktor deklariert wird, so wird für die Klasse automatisch ein parameterloser Konstruktor generiert, der nichts weiter macht als den Konstruktor der Oberklasse aufzurufen. Sobald die Klasse einen Konstruktor deklariert, so wird kein Konstruktor für sie mehr generiert.
Ein solcher automatisch generierter Konstruktor wird in Java (JLS10, 8.8.9) als Fehlkonstruktor bezeichnet.
Main.java
class Object
{ }public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object = new Object(); }}transcriptKonsole
javap Main
javap -c Main
Main.java
class Object
{ public Object( final int x )
{ }}public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object = new Object(); }}transcript
Main.java:7: error: constructor Object in class Object cannot be applied to given types;
{ final Object object = new Object(); }}
^
required: int
found: no arguments
reason: actual and formal argument lists differ in length
1 errorMain.java
class Object
{ public Object()
{ }
public Object( final int x )
{ }}public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object = new Object(); }}transcript
Ein Fehlkonstruktor (“default constructor ”) wird im Deutschen auch manchmal als „Standardkonstruktor“ bezeichnet, was mißverständlich sein kann. Auch im Englischen wird “standard constructor ” manchmal für “default constructor ” verwendet.
Kombinierende Klassendeklarationen
Alle Beispiele lassen sich auch in den „kombinierten Stil“ mit nur einer Klassendeklaration, in welcher statische und nicht-statische Einträge gemeinsam vorkommen, umschreiben, beispielsweise:
Main.java
public final class Main
{ final int a;
final int b;Main( final int a, final int b )
{ final Main object1 = new Main( 7, 8 );
this.a = a; this.b = b; }public static void main( final java.lang.String[] args )
{ final Main main = new Main( 7, 8 );
java.lang.System.out.println( main.a );
java.lang.System.out.println( main.b ); }}java.lang.System.out
7
8
Der Vorgabekonstruktor
Falls eine Klasse keine Konstruktordeklarationen enthält wird vom Java -Übersetzer stillschweigend ein parameterloser Vorgabekonstruktor definiert, der allerdings bei den hier beschrieben Klassen nichts macht, also wie ein Konstruktor mit einem leeren Rumpf "{}" ist.
Ein Exemplar der Klasse "MyClass" kann in dem Programm "DefaultConstructor.java" erzeugt werden, obwohl dort kein Konstruktor definiert wurde, weil der Vorgabekonstruktor verwendet wird.
DefaultConstructor.java
class DefaultConstructor
{ public static void main( final String[] args )
{ final MyClass m = new MyClass(); }}MyClass.java
class MyClass{}
Der Java-Disassembler "javap" zeigt den vom Java-Übersetzer "javac" erzeugten Vorgabekonstruktor der Klasse "MyClass" an.
Konsole
javap MyClass
Compiled from MyClass.java
class MyClass extends java.lang.Object {
MyClass();
}
Sobald jedoch irgendein Konstruktor explizit deklariert wird, wird kein Vorgabekonstruktor mehr erzeugt.
NoDefaultConstructor.java
class NoDefaultConstructor
{ public static void main( final String[] args )
{ final MyClass1 m = new MyClass1(); }}MyClass1.java
class MyClass1
{ MyClass1( final double i ){} }Konsole
javac "MyClass.java"
javac "NoDefaultConstructor.java"
NoDefaultConstructor.java:3: cannot resolve symbol
symbol : constructor MyClass1 ()
location: class NoDefaultConstructor
{ final MyClass1 m = new MyClass1(); }}
^
Mangels eines parameterlosen Konstruktors kann der argumentlose Exemplarerzeugungsausdruck "new MyClass1()" der Quelldatei "NoDefaultConstructor.java" nicht ausgewertet werden.
Die Klasse "MyClass" enthält nur einen Konstruktor mit einem double -Parameter, aber keinen parameterlosen Konstruktor, der zu dem argumentlosen Exemplarerzeugungsausdruck passen würde.
Konsole
javap MyClass
Compiled from MyClass.java
class MyClass extends java.lang.Object {
MyClass(double);
}
Das qualifizierte »this«
Schlüsselwort "this"
Im Konstruktor steht das Schlüsselwort "this" für das zu initialisierende Exemplar. In dem Konstruktor der Klasse "Switch" der Quelldatei "Switch.java" könnte als linke Seite der Zuweisung anstelle des Feldzugriffs "this.on" auch der Bezeichner "on" stehen, da auch so eindeutig das Feld "on" des zu initialisierenden Exemplars bezeichnet wird.
Konstruktoren und »final«
Main.java
public final class Main
{ final int a;
Main()
{ java.lang.System.out.println( this.a );
a = 2;
java.lang.System.out.println( this.a ); }public static void main( final java.lang.String[] args )
{ new Main(); }}transcript
0
2
Initialisierung konstanter Felder
Auch konstante Felder können im Konstruktor noch durch eine Zuweisung initialisiert werden, was in einer normalen Methode natürlich nicht möglich ist.
ConstantClient.java
public final class ConstantClient
{ public static void main( final String[] args )
{ final Constant c = new Constant();
System.out.println( c.getPi() ); }}
class Constant
{ final private double pi;
Constant(){ pi = 4 * Math.atan( 1. ); }
double getPi(){ return pi; }}
Spionage
Initialisierung und Konstruktion
Aufrufe andere Konstruktoren derselben Klasse
Redundanzarme Konstruktoren
Aus einem Konstruktor heraus kann ein anderer Konstruktor aufgerufen werden, indem das Schlüsselwort "this" auf Argumente angewendet wird. Soll etwa ein Konstruktor "Switch( boolean state )" angewendet werden, so kann dies mit der Anweisung "this( true );" geschehen.
Solch eine Anweisung muß die erste im Konstruktorrumpf sein.
- 〈ConstructorBody 〉 ::=
- "{" [〈ExplicitConstructorInvocation 〉] [〈BlockStatements 〉] "}".
Gelegentlich wird empfohlen, auf diese Weise Konstruktoren mit weniger Parametern auf Konstruktoren mit mehr Parametern zurückzuführen, um Wiederholung von Code (Redundanz) zu vermeiden.
Switch2.java
class Switch2
{ private boolean on;
Switch2(){ this( true ); }
Switch2( final boolean on ){ this.on = on; }
void switchOn (){ on = true; }
void switchOff(){ on = false; }
boolean isOn(){ return on; }}
Aus einem Konstruktor heraus können auch Methoden aufgerufen werden. Es ist noch universeller, den gemeinsamen Teil mehrere Konstruktoren in einer Methode zu lagern.
Switch3.java
class Switch3
{ private boolean on;
private void init( final boolean on ){ this.on = on; }
Switch3(){ this.init( true ); }
Switch3( final boolean on ){ this.init( on ); }
void switchOn (){ on = true; }
void switchOff(){ on = false; }
boolean isOn(){ return on; }}
Eine statische Methode kann von Java ™ oft noch etwas schneller ausgeführt werden als eine Exemplarmethode. Da die Verwendung der statischen Methode hier die Schnittstelle nicht beeinflußt, kommt sie hier in Frage, obwohl solche statischen Methoden sonst in vielen Fälle mit Prinzipien der objektorientierten Programmierung nicht verträglich sind.
Switch4.java
class Switch4
{ private boolean on;
private static void init
( final Switch4 self, final boolean on ){ self.on = on; }
Switch4(){ init( this, true ); }
Switch4( final boolean on ){ init( this, on ); }
void switchOn (){ on = true; }
void switchOff(){ on = false; }
boolean isOn(){ return on; }}
Bequemlichkeitskonstruktoren
Beispiel
Übungsfragen
? Übungsfrage
? Übungsfrage
Übungsaufgaben
/ Konstruktoren
Schreiben Sie einen Konstruktor und einen Getter, so daß der Getter einen int-Wert ausgibt, welcher dem Objekt bei der Erzeugung übergeben wurde.
Main.java
public final class Main
{/* Beginn der "Objektklasse Main" mit nicht-statischen Einträgen */
…
/* Beginn der "Verzeichnisklasse Main" mit statischen Einträgen */
…
public static void main( final java.lang.String[] args )
{ final Main o = new Main( 2 ); /* Anlegen eines Objekts der "Objektklasse Main" */
final Main x = new Main( 3 ); /* Anlegen eines Objekts der "Objektklasse Main" */
java.lang.System.out.println( o.get() );
java.lang.System.out.println( x.get() ); }}java.lang.System.out
2
3
/ Objekte mit Gedächtnis
Schreiben Sie eine Implementation der Schnittstelle »java.lang.Runnable«, die eine Methode mit dem Deskriptor »void run()« deklariert, welche eine Zahl ausgibt, die ihr zuvor im Konstruktor übergeben wurde.
(Der Deskriptor einer Methode ist die Kombination aus dem Aufruftyp dieser Methode und der Signatur dieser Methode. Die Signatur einer Methode ist die Kombination aus dem Methodenbezeichner dieser Methode und der eingeklammerten Parameterliste dieser Methode.)
/ Objekte zählen
Schreiben Sie eine Deklaration einer Klasse, die während eines Programmablaufs mitzählt, wie viele Objekte dieser Klasse angelegt wurden. Die Methode »creationCount()« soll dann den Wert des Zählers ergeben.
/ Kopierkonstruktoren
Schreiben Sie einen „Kopierkonstruktor“ indem Sie die drei Punkte jeweils durch einen bestimmten Quelltext ersetzen!
Main.java
final class Object
{ final int a;
final int b;
public Object( final int a, final int b )
{ this.a = a; this.b = b; }
public Object( final … )
{ … }}public final class Main
{ public static void main( final java.lang.String[] args )
{ final Object object0 = new Object( 9, 1 );
java.lang.System.out.println( object0.a );
java.lang.System.out.println( object0.b );
final Object object1 = new Object( object0 );
java.lang.System.out.println( object1.a );
java.lang.System.out.println( object1.b ); }}java.lang.System.out
9
1
9
1