Wir kennen dies im Prinzip schon für die klassenverwandten
Datentypen String und Feld: So wird ein
double
-Feld durch double[] Vektor1=new
double[6];
deklariert und initialisiert. Seine Länge (6) kann
durch Vektor1.length
ermittelt werden,
d.h. length
ist der Name eines Attributs aller Objekte
vom Typ eines Feldes. Entsprechend haben wir gesehen, dass
equals()
(und length()
) eine Methode von
Variablen des Typs String
bezeichnet.
Die Daten (Attribute) von Objekten einer Klasse, die komplexe Zahlen
darstellt, werden
also sinnvollerweise Real- (Re) und Imaginärteil
(Im) sein. In der darauf folgenden
Applikationsklasse Komplex1
wird eine
komplexe Zahl als Objekt der Klasse KomplexeZahl1
durch
Übergabe des Real- und des Imaginärteils an den Konstruktor
konstruiert. Spätere Erweiterungen dieser Klasse werden KomplexeZahl2,
KomplexeZahl3, ...
heißen.
class KomplexeZahl1{ //Daten: double Re, Im; //Konstruktor: KomplexeZahl1(double x, double y){ Re=x; Im=y; }//Ende Konstruktor }//Ende class KomplexeZahl1
Diese Klasse kann zwar kompiliert, aber nicht ausgeführt werden,
da es sich nicht um eine Applikationsklasse handelt (sie enthält
keine main()
-Methode).
Eine Applikationsklasse, die ein Objekt vom Typ
KomplexeZahl1
instanziert, könnte so aussehen:
Die wichtigste Zeile ist die, in der der Konstruktor in Verbindung mit dem new-Operator eingesetzt wird. Der Bezeichner des Konstruktors muss stets identisch mit dem Namen der zugehörigen Klasse sein.class Komplex1{ static void main(String[] args){ KomplexeZahl1 z = new KomplexeZahl1(1.1, 2.3); }//Ende main()( }//Ende class Komplex1
Die imaginäre Einheit i hätte man mit
KomplexeZahl1 i =
new KomplexeZahl1(0, 1);
konstruieren können.
Jetzt geben wir eine Applikationsklasse an, die eine Methode vorsieht, die komplexe Zahlen miteinander multipliziert und eine solche Multiplikation auch für konkrete Zahlen ausführt:
Beachten Sie, dass sowohl die beiden Parameter als auch der Rückgabewert der Methodeclass komplex2{ static KomplexeZahl1 mal(KomplexeZahl1 z1, KomplexeZahl1 z2){ double w1, w2; w1=z1.Re*z2.Re-z1.Im*z2.Im; w2=z1.Re*z2.Im+z1.Im*z2.Re; return new KomplexeZahl1(w1,w2); }//Ende mal() static void main(String[] args){ KomplexeZahl1 z1 = new KomplexeZahl1(1.1, 2.3); KomplexeZahl1 z2 = new KomplexeZahl1(1.7, -2.1); KomplexeZahl1 w=mal(z1,z2); }//Ende main()( }//Ende class Komplex2
mal()
vom Typ der
Klasse KomplexeZahl1
sind. Die Rückgabe kann ohne
Deklaration einer Variable diesen Typs erfolgen.
main()
-Block wird das Produkt w
nicht durch einen Konstruktor
initialisiert, sondern durch Ausführung der Methode
mal()
.
Achten Sie auf die Punktnotation,
z.B. in z1.Re, wenn auf die Daten
eines Objektes zugegriffen werden soll. Auf den Imaginärteil des Produktes
kann man mittels w.Im zugreifen.
void
vermerkt wird. Wie bei Methoden kann man
auch Konstruktoren überladen, d.h.
es kann mehrere Konstruktoren geben. Diese müssen aber gleiche Namen
haben, sich jedoch in der Parameterliste unterscheiden, s.u.
Zuweilen möchte man den Parametern eines Konstruktors den gleichen Namen geben wie sie die Daten der Klasse haben. Dies geht unter Verwendung des Schlüsselworts this:
Die Verwendung von this bedarf einer Erklärung: Wenn die Bezeichner von Parametern von Konstruktoren mit denen von Daten der Klasse identisch sind, haben die Parameter als lokale Parameter Vorrang. Um auf die Daten zugreifen zu können, muss man this in der Punktnotation voranstellen, welches stellvertretend für den Bezeichner eines später zu konstruierenden Objektes steht.class KomplexeZahl2{ //Daten: double Re, Im; //Konstruktor: KomplexeZahl2(double Re, double Im){ this.Re=Re; this.Im=Im; }//Ende Konstruktor }//Ende class KomplexeZahl2
Will man z.B. ausschließen, dass ein Objekt namens z
der Klasse komplexeZahl1
nachträglich z.B. durch die
Anweisungen z.Re=-7.12
verändert wird, sollte
man dies dadurch verhindern, dass Daten den Zusatz private erhalten und sie damit für andere
Klassen nicht direkt zugänglich (unsichtbar) gemacht werden:
Allerdings ruft dann die Zeile
class komplexeZahl
.......
//Daten:
private double Re, Im;
......
System.out.println("Re z="+z.Re+"
Im z="+z.Im);
in einer Applikationsklasse die
Compiler-Fehlermeldung Variable Re in
class KomplexeZahl not accessible from class
Komplex2
hervor. Will man wirklich den direkten
Zugriff auf die Daten
Re, Im
verbieten, aber doch zulassen, dass sie gelesen werden
können, muss man weitere
öffentliche Methoden in der Klasse komplexeZahl
zur
Verfügung stellen, etwa so
Jetzt erreicht die folgende Applikationsklasse das gleiche Ziel wie
die Klasse
//KomplexeZahl3
......
private double Re, Im;
......
//Methoden:
....
public double getRe(){
return Re;
}
....
}//Ende class
Komplex2
:
Auf jeden Fall sollte die Klasse
class Komplex3{
static void main(String[] args){
KomplexeZahl3 z =
new KomplexeZahl3(1.1, 2.3);
System.out.println("Re z="+
z.getRe()+" Im z="+z.getIm());
}//Ende main()
}//Ende class Komplex3
KomplexeZahl
und ihr
Konstruktor öffentlich (public) sein, was schon dadurch erreicht
wird, dass der Zusatz weggelassen wird (wie bisher). Das bedeutet,
dass jede andere Klasse ein Objekt des Typs KomplexeZahl
initialisieren und seine öffentlichen Methoden benutzen kann.
Ein wesentlicher Vorteil bei einer Einschränkung von Zugriffsrechten bzw. der Sichtbarkeit ist, dass der Benutzer sich nur die öffentlichen Daten, Klassen und Methoden merken muss. Will man z.B. von den ungeheuer mächtigen Grafikklassen von Java Gebrauch machen, muss man in jedem Detail die Bezeichner der öffentlichen Klassen, ihrer öffentlichen Daten und Methoden, aber auch den Typ dieser Daten und die Paramterliste dieser Methoden kennen, im Zweifelsfall nachschlagen können oder auswendig lernen. Die intern vom Programmierer verwendeten nicht öffentlichen Klassen, Daten und Methoden interessieren nicht!
Neben private, public ist noch der Zusatz protected wichtig. Er tritt aber nur in Verbindung mit Unterklassen auf und erlaubt die Sichtbarkeit innerhalb aller Unterklassen.
Winkel
mit dem Attribut double w
(dem
eigentlichen Winkel im Bogenmaß) ein, dessen Konstruktor dafür sorgt,
dass w
in [0, 2*PI) liegt:
Jetzt können wir einen zweiten Konstruktor in der Klasseclass Winkel{ private double phi; Winkel(double a){ while (a>= 2*Math.PI) a-=Math.2*PI; while (a<0) a+=2*Math.PI); phi=a; }//Ende Konstruktor Winkel() public double getphi(){ return phi; } }//Ende class Winkel
komplexeZahl
vorsehen, dem Polarkoordinaten
übergeben werden und der den ersten Konstruktor überlädt:
Beachten Sie, dass beide Konstruktoren den gleichen Namen haben, dass sich ihre Parametertypen aber unterscheiden. Hätte ich nicht die Klasseclass KomplexeZahl4{ private double Re, Im; //Konstruktor 1 KomplexeZahl4(double x, double y){ Re=x; Im=y; } //Konstruktor 2 KomplexeZahl4(double r, Winkel W){ double phi=W.getphi(); Re=r*Math.cos(phi); Im=r*Math.sin(phi); } public double getRe(){ ... } }//Ende class KomplexeZahl4
Winkel
eingeführt, sondern einen Winkel als
einfache double
-Zahl angesehen, hätten die beiden
Konstruktoren jeweils zwei double
-Parameter und
hätten nicht unterschieden werden können. Das kann
natürlich nicht ausschlaggebender Grund sein, die Klasse
Winkel
einzuführen.